diff --git a/.gitattributes b/.gitattributes index 590f276f506..a2047fd34cc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ /.github/ export-ignore /benchmark/ export-ignore /core-tests/ export-ignore -/.travis.yml export-ignore *.h linguist-language=cpp diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 0ba2e94f203..bf6908e537a 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,4 +1,4 @@ -Please answer these questions before submitting your issue. Thanks! +Please answer these questions before submitting your issue. 1. What did you do? If possible, provide a simple script for reproducing the error. diff --git a/.github/WORKFLOW-PARAMETERS.md b/.github/WORKFLOW-PARAMETERS.md new file mode 100644 index 00000000000..91289839d12 --- /dev/null +++ b/.github/WORKFLOW-PARAMETERS.md @@ -0,0 +1,17 @@ +# Workflow Parameters +Adding parameters in the Git commit log can control the workflow. + +## --filter +This parameter specifies which workflows to run, instead of running all of them. +The command format is: `--filter=[flow1][flow2][flow...]` , +and it supports setting multiple workflows, with names matching the filename of the yml file. + +## --valgrind +Setting this parameter will cause the test program to be run with `valgrind`. + +```shell +git commit -m "commit message --filter=[core][unit] --valgrind" +``` + +## --asan +Setting this parameter will cause the test program to be run with `ASAN`. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..5ace4600a1f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile index ca4288faf3a..494bb252deb 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -1,23 +1,23 @@ ARG PHP_VERSION ARG ALPINE_VERSION -FROM hyperf/hyperf:${PHP_VERSION}-alpine-v${ALPINE_VERSION}-dev +FROM phpswoole/php:${PHP_VERSION}-alpine -LABEL maintainer="Swoole Team " version="1.0" license="MIT" +LABEL maintainer="Swoole Team " version="1.0" license="Apache2" ARG PHP_VERSION -COPY . /opt/www +COPY . /swoole -WORKDIR /opt/www +WORKDIR /swoole RUN set -ex \ && phpize \ - && ./configure --enable-openssl --enable-http2 --enable-swoole-curl --enable-swoole-json \ - && make -s -j$(nproc) && make install \ - && echo "extension=swoole.so" > /etc/php${PHP_VERSION%\.*}/conf.d/50_swoole.ini \ - # check - && php -v \ - && php -m \ - && php --ri swoole \ - && echo -e "\033[42;37m Build Completed :).\033[0m\n" + && ./configure --enable-openssl --enable-swoole-curl \ + && make -s -j$(nproc) && make install + +RUN echo "extension=swoole.so" > "/usr/local/etc/php/conf.d/swoole.ini" +RUN php -v +RUN php -m +RUN php --ri swoole +RUN echo -e "\033[42;37m Build Completed :).\033[0m\n" diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml new file mode 100644 index 00000000000..1c228f05ab3 --- /dev/null +++ b/.github/workflows/core.yml @@ -0,0 +1,74 @@ +name: Core Tests + +on: [ push, pull_request ] + +env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + +jobs: + build: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')" + timeout-minutes: 10 + services: + tinyproxy: + image: "vimagick/tinyproxy" + ports: + - 8888:8888 + nginx: + image: "nginx" + ports: + - "80:80" + env: + NGINX_PORT: "[::]:80" + socks5: + image: "xkuma/socks5" + ports: + - 8080:1080 + env: + PROXY_USER: user + PROXY_PASSWORD: password + PROXY_SERVER: 0.0.0.0:1080 + + steps: + - uses: actions/checkout@v4 + + - name: install dependencies + run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev + + - name: configure + run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-openssl + + - name: make + run: | + cmake . -D CODE_COVERAGE=ON -D enable_thread=1 + make VERBOSE=1 -j $(nproc) lib-swoole + + - name: make test with valgrind + if: "contains(github.event.head_commit.message, '--valgrind')" + run: | + sudo apt install -y valgrind + cd core-tests && SWOOLE_VALGRIND=1 ./run.sh + + - name: make test with asan + if: "contains(github.event.head_commit.message, '--asan')" + run: | + cd core-tests && SWOOLE_ENABLE_ASAN=1 ./run.sh + + - name: make test + run: + cd core-tests && ./run.sh + + - name: run coverage + shell: bash + run: sudo apt-get install lcov && + sudo lcov --directory . --capture --output-file coverage.info && + sudo lcov --remove coverage.info "${{runner.workspace}}/swoole-src/include/*" '/usr/*' --output-file coverage.info && + sudo lcov --list coverage.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.info + fail_ci_if_error: true diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index b36b8bb7d85..c223a1333b7 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -19,11 +19,11 @@ jobs: if: github.repository_owner == 'swoole' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "7.4" + php-version: "8.0" - name: Download and install Coverity Build Tool run: | diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml deleted file mode 100644 index 167b899417f..00000000000 --- a/.github/workflows/cygwin.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: cygwin - -on: - push: - tags: - - 'v*' - -jobs: - build: - if: github.repository_owner == 'swoole' - runs-on: windows-latest - outputs: - version: ${{ steps.swoole_info.outputs.version }} - body: ${{ steps.swoole_info.outputs.upload_url }} - steps: - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 - with: - platform: x64 - packages: cmake php php-devel gcc-g++ openssl libssl-devel libcurl-devel libpcre2-devel wget tar php-curl php-json php-sockets php-mysqli php-bcmath php-bz2 php-calendar php-ctype php-phar php-posix php-pspell php-exif php-recode php-fileinfo php-simplexml php-gd php-soap php-gettext php-sockets php-gmp php-gv php-iconv php-intl php-json php-tokenizer php-mbstring php-ming php-xmlreader php-xmlwriter php-opcache php-xsl php-zip php-pdo_mysql php-zlib php-redis - - - name: Get Swoole Info - id: swoole_info - run: | - wget https://api.github.com/repos/swoole/swoole-src/releases/latest -O latest - $g=cat .\latest | ConvertFrom-Json - $version=$g.tag_name - $upload_url=$g.upload_url - echo $version - echo $upload_url - echo "::set-output name=version::$version" - echo "::set-output name=upload_url::$upload_url" - - - name: Build Swoole - run: | - wget https://github.com/swoole/swoole-src/archive/${{ steps.swoole_info.outputs.version }}.tar.gz -O swoole.tar.gz - mkdir -p swoole - tar -xf swoole.tar.gz -C swoole --strip-components=1 - rm swoole.tar.gz - cd swoole - /bin/phpize - ./configure --enable-openssl --with-openssl_dir=/usr --enable-http2 --enable-mysqlnd - make -j6 - cp modules/swoole.dll /usr/lib/php/20180731 - echo 'extension=swoole.dll' > /etc/php.d/swoole.ini - php -v - php -m - php --ri swoole - php --ini - cd .. - shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' - - - name: Run build - env: - VERSION: ${{ steps.swoole_info.outputs.version }} - run: | - C:\tools\php\php.exe -v - C:\tools\php\php.exe -m - wget https://github.com/lufei/cygwin/archive/refs/heads/master.tar.gz -O cygwin.tar.gz - mkdir -p swoole-cygwin - tar -xf cygwin.tar.gz -C swoole-cygwin --strip-components=1 - rm cygwin.tar.gz - cd swoole-cygwin - C:\tools\php\php.exe build.php - cd .. - - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.swoole_info.outputs.upload_url }} - asset_path: .\swoole-cygwin\swoole-cygwin-${{ steps.swoole_info.outputs.version }}.zip - asset_name: swoole-cygwin-${{ steps.swoole_info.outputs.version }}.zip - asset_content_type: application/zip diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index eb1bd6f52f9..b9232561366 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,116 +1,85 @@ -name: ext-swoole +name: Compile Tests -on: [push, pull_request] +on: [ push, pull_request ] + +env: + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" jobs: build-ubuntu-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[ext]')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: install-deps - run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - - name: phpize - run: phpize - - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build8 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - build-ubuntu-old: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - - name: install-deps - run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - - name: phpize - run: phpize - - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + - uses: actions/checkout@v4 + - name: install-deps + run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev + - name: phpize + run: phpize + - name: build1 + run: ./configure && + make clean && make -j$(nproc) + - name: build2 + run: ./configure --enable-sockets && + make clean && make -j$(nproc) + - name: build3 + run: ./configure --enable-sockets --enable-mysqlnd && + make clean && make -j$(nproc) + - name: build5 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(nproc) + - name: build6 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build7 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build8 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && + make clean && make -j$(nproc) + - name: build with thread context + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log --enable-thread-context && + make clean && make -j$(nproc) build-macos-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[macos]')" runs-on: macos-latest steps: - - name: install dependencies - run: brew install openssl && brew link openssl && brew install curl && brew link curl && brew install c-ares && brew link c-ares - - uses: actions/checkout@v1 - - name: phpize - run: phpize - - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl - --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + - name: install dependencies + run: brew reinstall php + - uses: actions/checkout@v4 + - name: phpize + run: phpize + - name: build1 + run: ./configure CPPFLAGS="${CPPFLAGS}" && make clean && make -j$(sysctl -n hw.ncpu) + - name: build2 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build3 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build5 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build6 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl + --enable-swoole-curl --enable-debug-log && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build7 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl + --enable-debug-log --enable-cares && + make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[alpine]')" runs-on: ubuntu-latest strategy: matrix: - php-version: [ '7.4', '8.0' ] - alpine-version: [ '3.11', '3.12', '3.13', '3.14' ] + php-version: [ '8.1', '8.2', '8.3', '8.4' ] max-parallel: 8 fail-fast: false steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: build run: | cp .github/workflows/alpine.Dockerfile alpine.Dockerfile - docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} --build-arg ALPINE_VERSION=${{ matrix.alpine-version }} + docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml new file mode 100644 index 00000000000..12c6523bea5 --- /dev/null +++ b/.github/workflows/framework.yml @@ -0,0 +1,122 @@ +name: Framework Tests + +on: + push: + pull_request: + +env: + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" + +jobs: + linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev valgrind + phpize + ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql + make -j$(nproc) + sudo make install + php -v + php -m + php --ini + php --ri swoole + + - name: Laravel Octane Tests + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.3' + run: | + git clone https://github.com/laravel/octane.git --depth=1 + cd octane/ + composer update --prefer-dist --no-interaction --no-progress + vendor/bin/phpunit + + - name: Hyperf Tests + if: matrix.framework == 'Hyperf' && matrix.php-version != '8.3' + env: + SW_VERSION: 'master' + MYSQL_VERSION: '5.7' + PGSQL_VERSION: '14' + run: | + git clone https://github.com/hyperf/hyperf.git --depth=1 + cd hyperf/ + composer update -o + ./.travis/requirement.install.sh + ./.travis/setup.services.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh + cp .travis/.env.example .env + export SWOOLE_BRANCH=${GITHUB_REF##*/} + if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine + else + .travis/run.test.sh + fi + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + + macos: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + phpize + ./configure CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares + make -j$(sysctl -n hw.ncpu) + sudo make install + php --ri swoole + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml new file mode 100644 index 00000000000..047bfdfca05 --- /dev/null +++ b/.github/workflows/iouring.yml @@ -0,0 +1,40 @@ +name: Linux io_uring Tests + +on: [push, pull_request] + +jobs: + test-linux: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')" + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_USE_IOURING=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh + diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml deleted file mode 100644 index 71afc15a01c..00000000000 --- a/.github/workflows/lib.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: lib-swoole - -on: [push, pull_request] - -env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - -jobs: - build: - runs-on: ubuntu-latest - - services: - tinyproxy: - image: "vimagick/tinyproxy" - ports: - - 8888:8888 - socks5: - image: "xkuma/socks5" - ports: - - 1080:1080 - - steps: - - uses: actions/checkout@v1 - - - name: install dependencies - run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev - - - name: configure - run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl - - - name: make - run: cmake . -DCODE_COVERAGE=ON && - make VERBOSE=1 -j && - sudo make install - - - name: make test - run: - cd core-tests && ./run.sh - - - name: run coverage - shell: bash - run: sudo apt-get install lcov && - sudo lcov --directory . --capture --output-file coverage.info && - sudo lcov --remove coverage.info "${{runner.workspace}}/swoole-src/include/*" '/usr/*' --output-file coverage.info && - sudo lcov --list coverage.info - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.info - fail_ci_if_error: true diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index dc42ad90314..00000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: mirror - -on: [ push, delete, create ] - -jobs: - gitee: - if: github.repository_owner == 'swoole' - runs-on: ubuntu-latest - steps: - - name: Configure Private Key - env: - SSH_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }} - run: | - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "StrictHostKeyChecking no" >> ~/.ssh/config - - - name: Push Mirror - env: - SOURCE_REPO: 'https://github.com/swoole/swoole-src.git' - DESTINATION_REPO: 'git@gitee.com:swoole/swoole.git' - run: | - git clone --mirror "$SOURCE_REPO" && cd `basename "$SOURCE_REPO"` - git remote set-url --push origin "$DESTINATION_REPO" - git fetch -p origin - git for-each-ref --format 'delete %(refname)' refs/pull | git update-ref --stdin - git push --mirror diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml deleted file mode 100644 index 35b68aa2af7..00000000000 --- a/.github/workflows/test-linux.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: test-linux - -on: [push, pull_request] - -jobs: - test-linux: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: [7.2, 7.3, 7.4, 8.0] - steps: - - uses: actions/checkout@v1 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: "${{ matrix.php }}" - - name: Show machine information - run: | - date - env - uname -a - ulimit -a - php -v - php --ini - ls -al - pwd - echo "`git log -20 --pretty --oneline`" - echo "`git log -10 --stat --pretty --oneline`" - - name: Run pecl-install.sh - run: | - sudo ${{runner.workspace}}/swoole-src/travis/pecl-install.sh - - name: Run simple-compile-on-github.sh - run: | - sudo ${{runner.workspace}}/swoole-src/travis/simple-compile-on-github.sh - - name: Run Swoole test - run: | - export TRAVIS_BRANCH=${GITHUB_REF##*/} - ${{runner.workspace}}/swoole-src/travis/route.sh diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml new file mode 100644 index 00000000000..749616aaa71 --- /dev/null +++ b/.github/workflows/thread.yml @@ -0,0 +1,42 @@ +name: Thread Support Tests + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[thread]')" + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + php: ['8.1-zts', '8.2-zts', '8.3-zts', '8.4-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + env: + phpts: ts + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run tests + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_THREAD=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 00000000000..a6d1ae06e89 --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,41 @@ +name: Unit Tests + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[unit]')" + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.4'] + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run pecl-install.sh + run: | + sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh + - name: Run unit tests + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/.github/workflows/xdebug.yml b/.github/workflows/xdebug.yml new file mode 100644 index 00000000000..8933683dccb --- /dev/null +++ b/.github/workflows/xdebug.yml @@ -0,0 +1,37 @@ +name: Xdebug Support Tests + +on: [push, pull_request] + +jobs: + tests: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[xdebug]')" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3'] + + name: PHP ${{ matrix.php }} - Swoole + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: phpize + ini-values: extension=swoole + coverage: xdebug + - name: Build Swoole + run: | + phpize + ./configure + make -j$(nproc) + sudo make install + - name: Check extension conflict + run: | + php -v + php -m + php --ri swoole diff --git a/.gitignore b/.gitignore index 3924cb00214..9068deb313d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,13 +8,13 @@ *.loT *.pid *.dep -/Debug/* modules/* /.deps /.libs/ /core /examples/core /Debug +/Release /CMakeFiles /cmake_install.cmake /CMakeCache.txt @@ -80,11 +80,25 @@ cmake-build-debug/ /.vs /configure.in /configure.ac + +# core-tests /core-tests/CMakeCache.txt /core-tests/CMakeFiles/ /core-tests/Makefile /core-tests/bin/ /core-tests/cmake_install.cmake +/core-tests/samples/Makefile +/core-tests/samples/cmake_install\.cmake +/core-tests/samples/CMakeCache\.txt +/core-tests/samples/bin/ +/core-tests/samples/CMakeFiles/ +/core-tests/.project +/core-tests/.cproject +/core-tests/compile_commands.json +/core-tests/samples/.project +/core-tests/fuzz/fuzz_results/ +/core-tests/fuzz/bin/ + /tools/vendor /tools/composer.lock /examples/wrapper/CMakeFiles @@ -92,11 +106,6 @@ cmake-build-debug/ /examples/wrapper/Makefile /examples/wrapper/server /examples/wrapper/cmake_install.cmake -core-tests/samples/Makefile -core-tests/samples/cmake_install\.cmake -core-tests/samples/CMakeCache\.txt -core-tests/samples/bin/ -core-tests/samples/CMakeFiles/ /out /gcov_result @@ -107,3 +116,5 @@ core-tests/samples/CMakeFiles/ *.info /html /tests/include/lib/vendor/ +/tests/include/lib/composer.lock +/scripts/data diff --git a/CMakeLists.txt b/CMakeLists.txt index 014d06a0961..008a4901608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 4.8.0) +set(SWOOLE_VERSION 6.0.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -cmake_minimum_required(VERSION 2.8) file(READ ./config.h SWOOLE_CONFIG_FILE) @@ -50,6 +50,8 @@ file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc thirdparty/hiredis/net.c thirdparty/hiredis/read.c thirdparty/hiredis/sds.c + thirdparty/swoole_http_parser.c + thirdparty/multipart_parser.c ) file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS include/*.h) file(GLOB_RECURSE HEAD_WAPPER_FILES FOLLOW_SYMLINKS include/wrapper/*.hpp) @@ -64,7 +66,7 @@ add_definitions(-DHAVE_CONFIG_H) # test #add_definitions(-DSW_USE_THREAD_CONTEXT) -include_directories(BEFORE ./include ./include/wrapper ext-src/ ./) +include_directories(BEFORE ./include ./include/wrapper ext-src/ thirdparty/ ./) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # find OpenSSL @@ -98,6 +100,11 @@ if (DEFINED enable_trace_log) add_definitions(-DSW_LOG_TRACE_OPEN) endif() +if (DEFINED enable_asan) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +endif() + execute_process(COMMAND php-config --includes OUTPUT_VARIABLE PHP_INCLUDES OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND php-config --extension-dir OUTPUT_VARIABLE PHP_EXTENSION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PHP_INCLUDES}") @@ -120,7 +127,8 @@ link_directories(${LIBRARY_OUTPUT_PATH}) add_library(lib-swoole SHARED ${SRC_LIST}) set_target_properties(lib-swoole PROPERTIES OUTPUT_NAME "swoole" VERSION ${SWOOLE_VERSION}) target_link_libraries(lib-swoole ${SWOOLE_LINK_LIBRARIES}) -if(CODE_COVERAGE) + +if (CODE_COVERAGE) target_link_libraries(lib-swoole coverage_config gcov) endif(CODE_COVERAGE) @@ -146,8 +154,6 @@ set(ext_src_list ${ext_cxx_files} thirdparty/php/sockets/conversions.cc thirdparty/php/sockets/sockaddr_conv.cc thirdparty/php/standard/proc_open.cc - thirdparty/swoole_http_parser.c - thirdparty/multipart_parser.c thirdparty/nghttp2/nghttp2_hd.c thirdparty/nghttp2/nghttp2_rcbuf.c thirdparty/nghttp2/nghttp2_helper.c @@ -161,10 +167,22 @@ set_target_properties(ext-swoole PROPERTIES PREFIX "") set_target_properties(ext-swoole PROPERTIES OUTPUT_NAME "swoole") add_dependencies(ext-swoole lib-swoole) -target_link_libraries(ext-swoole swoole) +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + +# find libpq +if (DEFINED libpq_dir) + target_include_directories(ext-swoole BEFORE ${LIBPQ_INCLUDE_DIRS}) +else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBPQ REQUIRED libpq) + target_include_directories(ext-swoole PRIVATE ${LIBPQ_INCLUDE_DIRS}) +endif() + +target_link_libraries(ext-swoole swoole pq) # install -INSTALL(CODE "MESSAGE(\"Are you run command using root user?\")") INSTALL(TARGETS ext-swoole LIBRARY DESTINATION ${PHP_EXTENSION_DIR}) INSTALL(TARGETS lib-swoole LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) INSTALL(FILES ${HEAD_FILES} DESTINATION include/swoole) diff --git a/LICENSE b/LICENSE index ad00f19ae93..16248b4d9b2 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright Tianfeng.Han [mikan.tenny@gmail.com] + Copyright Tianfeng.Han [rango@swoole.com] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README-CN.md b/README-CN.md deleted file mode 100644 index d742a3af669..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,547 +0,0 @@ -[English](./README.md) | 中文 - -

-Swoole Logo -

- -[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) -[![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) -[![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -[![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) - -**Swoole是一个C++编写的基于异步事件驱动和协程的并行网络通信引擎,为PHP提供高性能网络编程支持** - -## ⚙️ 快速启动 - -可以直接使用 [Docker](https://github.com/swoole/docker-swoole) 来执行Swoole的代码,例如: - -```bash -docker run --rm phpswoole/swoole "php --ri swoole" -``` - -具体的使用方式可以查看:[如何使用此镜像](https://github.com/swoole/docker-swoole#how-to-use-this-image) 。 - -或者可以在Swoole官网提供的 [在线编程](https://www.swoole.com/coding) 页面运行代码以及官网提供的示例代码。 - -## ✨ 事件驱动 - -Swoole中的网络请求处理是基于事件的,并且充分利用了底层的 epoll/kqueue 实现,使得为数百万个请求提供服务变得非常容易。 - -Swoole4使用全新的协程内核引擎,现在它拥有一个全职的开发团队,因此我们正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 - -## ⚡️ 协程 - -Swoole4或更高版本拥有高可用性的内置协程,您可以使用完全同步的代码来实现异步性能,PHP代码没有任何额外的关键字,底层会自动进行协程调度。 - -开发者可以将协程理解为超轻量级的线程, 你可以非常容易地在一个进程中创建成千上万个协程。 - -### MySQL客户端 - -并发1万个请求从MySQL读取海量数据仅需要0.2秒 - -```php -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - $mysql = new Swoole\Coroutine\MySQL; - $mysql->connect([ - 'host' => '127.0.0.1', - 'user' => 'root', - 'password' => 'root', - 'database' => 'test' - ]); - $statement = $mysql->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $result = $statement->execute(); - assert(count($result) > 0); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -### 混合服务器 - -你可以在一个事件循环上创建多个服务:TCP,HTTP,Websocket和HTTP2,并且能轻松承载上万请求。 - -```php -function tcp_pack(string $data): string -{ - return pack('N', strlen($data)) . $data; -} -function tcp_unpack(string $data): string -{ - return substr($data, 4, unpack('N', substr($data, 0, 4))[1]); -} -$tcp_options = [ - 'open_length_check' => true, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_body_offset' => 4 -]; -``` - -```php -$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE); -$server->set(['open_http2_protocol' => true]); -// http && http2 -$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $response->end('Hello ' . $request->rawcontent()); -}); -// websocket -$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { - $server->push($frame->fd, 'Hello ' . $frame->data); -}); -// tcp -$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP); -$tcp_server->set($tcp_options); -$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) { - $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); -}); -$server->start(); -``` -### 多种客户端 - -不管是DNS查询抑或是发送请求和接收响应,都是协程调度的,不会产生任何阻塞。 - -```php -go(function () { - // http - $http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501); - assert($http_client->post('/', 'Swoole Http')); - var_dump($http_client->body); - // websocket - $http_client->upgrade('/'); - $http_client->push('Swoole Websocket'); - var_dump($http_client->recv()->data); -}); -go(function () { - // http2 - $http2_client = new Swoole\Coroutine\Http2\Client('localhost', 9501); - $http2_client->connect(); - $http2_request = new Swoole\Http2\Request; - $http2_request->method = 'POST'; - $http2_request->data = 'Swoole Http2'; - $http2_client->send($http2_request); - $http2_response = $http2_client->recv(); - var_dump($http2_response->data); -}); -go(function () use ($tcp_options) { - // tcp - $tcp_client = new Swoole\Coroutine\Client(SWOOLE_TCP); - $tcp_client->set($tcp_options); - $tcp_client->connect('127.0.0.1', 9502); - $tcp_client->send(tcp_pack('Swoole Tcp')); - var_dump(tcp_unpack($tcp_client->recv())); -}); -``` - -### 通道 - -通道(Channel)是协程之间通信交换数据的唯一渠道, 而协程+通道的开发组合即为著名的CSP编程模型。 - -在Swoole开发中,Channel常用于连接池的实现和协程并发的调度。 - -#### 连接池最简示例 - -在以下示例中,我们并发了一千个redis请求,通常的情况下,这已经超过了Redis最大的连接数,将会抛出连接异常, 但基于Channel实现的连接池可以完美地调度请求,开发者就无需担心连接过载。 - -```php -class RedisPool -{ - /**@var \Swoole\Coroutine\Channel */ - protected $pool; - - /** - * RedisPool constructor. - * @param int $size max connections - */ - public function __construct(int $size = 100) - { - $this->pool = new \Swoole\Coroutine\Channel($size); - for ($i = 0; $i < $size; $i++) { - $redis = new \Swoole\Coroutine\Redis(); - $res = $redis->connect('127.0.0.1', 6379); - if ($res == false) { - throw new \RuntimeException("failed to connect redis server."); - } else { - $this->put($redis); - } - } - } - - public function get(): \Swoole\Coroutine\Redis - { - return $this->pool->pop(); - } - - public function put(\Swoole\Coroutine\Redis $redis) - { - $this->pool->push($redis); - } - - public function close(): void - { - $this->pool->close(); - $this->pool = null; - } -} - -go(function () { - $pool = new RedisPool(); - // max concurrency num is more than max connections - // but it's no problem, channel will help you with scheduling - for ($c = 0; $c < 1000; $c++) { - go(function () use ($pool, $c) { - for ($n = 0; $n < 100; $n++) { - $redis = $pool->get(); - assert($redis->set("awesome-{$c}-{$n}", 'swoole')); - assert($redis->get("awesome-{$c}-{$n}") === 'swoole'); - assert($redis->delete("awesome-{$c}-{$n}")); - $pool->put($redis); - } - }); - } -}); -``` - -#### 生产和消费 - -Swoole的部分客户端实现了defer机制来进行并发,但你依然可以用协程和通道的组合来灵活地实现它。 - -```php -go(function () { - // User: I need you to bring me some information back. - // Channel: OK! I will be responsible for scheduling. - $channel = new Swoole\Coroutine\Channel; - go(function () use ($channel) { - // Coroutine A: Ok! I will show you the github addr info - $addr_info = Co::getaddrinfo('github.com'); - $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); - }); - go(function () use ($channel) { - // Coroutine B: Ok! I will show you what your code look like - $mirror = Co::readFile(__FILE__); - $channel->push(['B', $mirror]); - }); - go(function () use ($channel) { - // Coroutine C: Ok! I will show you the date - $channel->push(['C', date(DATE_W3C)]); - }); - for ($i = 3; $i--;) { - list($id, $data) = $channel->pop(); - echo "From {$id}:\n {$data}\n"; - } - // User: Amazing, I got every information at earliest time! -}); -``` - -### 定时器 - -```php -$id = Swoole\Timer::tick(100, function () { - echo "⚙️ Do something...\n"; -}); -Swoole\Timer::after(500, function () use ($id) { - Swoole\Timer::clear($id); - echo "⏰ Done\n"; -}); -Swoole\Timer::after(1000, function () use ($id) { - if (!Swoole\Timer::exists($id)) { - echo "✅ All right!\n"; - } -}); -``` - -#### 使用协程方式 - -```php -go(function () { - $i = 0; - while (true) { - Co::sleep(0.1); - echo "📝 Do something...\n"; - if (++$i === 5) { - echo "🛎 Done\n"; - break; - } - } - echo "🎉 All right!\n"; -}); -``` - -### 命名空间 - -Swoole提供了多种类命名规则以满足不同开发者的爱好 - -1. 符合PSR规范的命名空间风格 -2. 便于键入的下划线风格 -3. 协程类短名风格 - -## 🔥 强大的运行时钩子 - -在最新版本的Swoole中,我们添加了一项新功能,使PHP原生的同步网络库一键化成为协程库。 - -只需在脚本顶部调用`Swoole\Runtime::enableCoroutine()`方法并使用`php-redis`,并发1万个请求从Redis读取数据仅需0.1秒! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - ($redis = new Redis)->connect('127.0.0.1', 6379); - for ($n = 100; $n--;) { - assert($redis->get('awesome') === 'swoole'); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -调用它之后,Swoole内核将替换ZendVM中的Stream函数指针,如果使用基于`php_stream`的扩展,则所有套接字操作都可以在运行时动态转换为协程调度的异步IO。 - -### 你可以在一秒钟里做多少事? - -睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端相互通信1万次......一切都在一个进程中完美完成! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - // i just want to sleep... - for ($c = 100; $c--;) { - go(function () { - for ($n = 100; $n--;) { - usleep(1000); - } - }); - } - - // 10k file read and write - for ($c = 100; $c--;) { - go(function () use ($c) { - $tmp_filename = "/tmp/test-{$c}.php"; - for ($n = 100; $n--;) { - $self = file_get_contents(__FILE__); - file_put_contents($tmp_filename, $self); - assert(file_get_contents($tmp_filename) === $self); - } - unlink($tmp_filename); - }); - } - - // 10k pdo and mysqli read - for ($c = 50; $c--;) { - go(function () { - $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); - $statement = $pdo->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $statement->execute(); - assert(count($statement->fetchAll()) > 0); - } - }); - } - for ($c = 50; $c--;) { - go(function () { - $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); - $statement = $mysqli->prepare('SELECT `id` FROM `user`'); - for ($n = 100; $n--;) { - $statement->bind_result($id); - $statement->execute(); - $statement->fetch(); - assert($id > 0); - } - }); - } - - // php_stream tcp server & client with 12.8k requests in single process - function tcp_pack(string $data): string - { - return pack('n', strlen($data)) . $data; - } - - function tcp_length(string $head): int - { - return unpack('n', $head)[1]; - } - - go(function () { - $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); - $socket = stream_socket_server( - 'tcp://0.0.0.0:9502', - $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx - ); - if (!$socket) { - echo "$errstr ($errno)\n"; - } else { - $i = 0; - while ($conn = stream_socket_accept($socket, 1)) { - stream_set_timeout($conn, 5); - for ($n = 100; $n--;) { - $data = fread($conn, tcp_length(fread($conn, 2))); - assert($data === "Hello Swoole Server #{$n}!"); - fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); - } - if (++$i === 128) { - fclose($socket); - break; - } - } - } - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - stream_set_timeout($fp, 5); - for ($n = 100; $n--;) { - fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); - $data = fread($fp, tcp_length(fread($fp, 2))); - assert($data === "Hello Swoole Client #{$n}!"); - } - fclose($fp); - } - }); - } - - // udp server & client with 12.8k requests in single process - go(function () { - $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9503); - $client_map = []; - for ($c = 128; $c--;) { - for ($n = 0; $n < 100; $n++) { - $recv = $socket->recvfrom($peer); - $client_uid = "{$peer['address']}:{$peer['port']}"; - $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; - assert($recv === "Client: Hello #{$id}!"); - $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); - } - } - $socket->close(); - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - for ($n = 0; $n < 100; $n++) { - fwrite($fp, "Client: Hello #{$n}!"); - $recv = fread($fp, 1024); - list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); - assert($address === '127.0.0.1' && (int)$port === 9503); - assert($recv === "Server: Hello #{$n}!"); - } - fclose($fp); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -## ⌛️ 安装 - -> 和任何开源项目一样, Swoole总是在**最新的发行版**提供最可靠的稳定性和最强的功能, 请尽量保证你使用的是最新版本 - -### 编译需求 - -+ Linux, OS X 系统 或 CygWin, WSL -+ PHP 7.2.0 或以上版本 (版本越高性能越好) -+ GCC 4.8 及以上 - -### 1. 使用PHP官方的PECL工具安装 (初学者) - -```shell -pecl install swoole -``` - -### 2. 从源码编译安装 (推荐) - -> 非内核开发研究之用途, 请下载[发布版本](https://github.com/swoole/swoole-src/releases)的源码编译 - -```shell -cd swoole-src && \ -phpize && \ -./configure && \ -make && sudo make install -``` - -#### 启用扩展 - -编译安装到系统成功后, 需要在`php.ini`中加入一行`extension=swoole.so`来启用Swoole扩展 - -#### 额外编译参数 - -> 使用例子: `./configure --enable-openssl --enable-sockets` - -+ `--enable-openssl` 或 `--with-openssl-dir=DIR` -+ `--enable-sockets` -+ `--enable-http2` -+ `--enable-mysqlnd` (需要 mysqlnd, 只是为了支持`mysql->escape`方法) -+ `--enable-swoole-json` -+ `--enable-swoole-curl` - -### 升级 - -> ⚠️ 如果你要从源码升级, 别忘记在源码目录执行 `make clean` - -1. `pecl upgrade swoole` -2. `cd swoole-src && git pull && make clean && make && sudo make install` -3. 如果你改变了PHP版本, 请重新执行 `phpize clean && phpize`后重新编译 - -## 💎 框架 & 组件 - -+ [**Hyperf**](https://github.com/hyperf/hyperf) 是一个高性能、高灵活性的协程框架,存在丰富的可能性,如实现分布式中间件,微服务架构等 -+ [**Swoft**](https://github.com/swoft-cloud) 是一个现代化的面向切面的高性能协程全栈组件化框架 -+ [**Easyswoole**](https://www.easyswoole.com) 是一个极简的高性能的框架,让代码开发就好像写`echo "hello world"`一样简单 -+ [**MixPHP**](https://github.com/mix-php/mix) 是一个功能强大的单线程协程框架,轻量、简单而优雅 -+ [**imi**](https://github.com/Yurunsoft/imi) 是基于 PHP Swoole 的高性能协程应用开发框架,它支持 HttpApi、WebSocket、TCP、UDP 服务的开发。 -+ [**Saber**](https://github.com/swlib/saber) 是一个人性化的高性能HTTP客户端组件,几乎拥有一切你可以想象的强大功能 -+ [**One**](https://github.com/lizhichao/one) 是一个极简高性能php框架,支持[swoole | php-fpm ]环境 - -## 🛠 开发 & 讨论 - -+ __中文文档__: -+ __Document__: -+ __IDE Helper & API__: -+ __调试工具__: -+ __中文社区及QQ群__: -+ __Twitter__: -+ __Slack Group__: - -## 🍭 性能测试 - -+ 在开源的 [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) 压测平台上,Swoole使用MySQL数据库压测的成绩一度位居首位, 所有IO性能测试都位列第一梯队。 -+ 你可以直接运行 [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) 来快速地测试出Swoole提供的Http服务在你的机器上所能达到的最大QPS - -## 🔰️ 安全问题 - -安全问题应通过电子邮件私下报告给Swoole开发团队[team@swoole.com](mailto:team@swoole.com)。您将会在24小时内收到回复,若由于某些原因您没有收到回复,请再次通过电子邮件跟进以确保我们收到了您的原始消息。 - -## 🖊️ 如何贡献 - -非常欢迎您对Swoole的开发作出贡献! - -你可以选择以下方式向Swoole贡献: - -+ [发布issue进行问题反馈和建议](https://github.com/swoole/swoole-src/issues) -+ 通过Pull Request提交修复 -+ 完善我们的文档和例子 - -## ❤️ 贡献者 - -项目的发展离不开以下贡献者的努力! [[Contributor](https://github.com/swoole/swoole-src/graphs/contributors)]. - - -## 📃 开源协议 - -Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/README.md b/README.md index 06a7f30c7b7..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -English | [中文](./README-CN.md) - -

-Swoole Logo -

+

+Swoole Logo
+ Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP. +

[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) [![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) +[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml) [![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) -**Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C++ for PHP.** +[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) +[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) +[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) ## ⚙️ Quick Start @@ -20,9 +23,112 @@ Run Swoole program by [Docker](https://github.com/swoole/docker-swoole) docker run --rm phpswoole/swoole "php --ri swoole" ``` -For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). +> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). + +## Documentation + + +### HTTP Service +```php +$http = new Swoole\Http\Server('127.0.0.1', 9501); +$http->set(['hook_flags' => SWOOLE_HOOK_ALL]); + +$http->on('request', function ($request, $response) { + $result = []; + Co::join([ + go(function () use (&$result) { + $result['google'] = file_get_contents("https://www.google.com/"); + }), + go(function () use (&$result) { + $result['taobao'] = file_get_contents("https://www.taobao.com/"); + }) + ]); + $response->end(json_encode($result)); +}); + +$http->start(); +``` + +### Concurrency +```php +Co\run(function() { + Co\go(function() { + while(1) { + sleep(1); + $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); + echo fread($fp, 8192), PHP_EOL; + } + }); + + Co\go(function() { + $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); + while(1) { + $conn = stream_socket_accept($fp); + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + while(true) { + $redis->subscribe(['test'], function ($instance, $channelName, $message) { + echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL; + }); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $count = 0; + while(true) { + sleep(2); + $redis->publish('test','hello, world, count='.$count++); + } + }); +}); +``` + +## Runtime Hook + +**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.** -Or code and run the Swoole program on the official website of Swoole. [Coding Online](https://www.swoole.com/coding) +### Supported extension/functions + +* `ext-curl` (Support `symfony` and `guzzle`) +* `ext-redis` +* `ext-mysqli` +* `ext-pdo_mysql` +* `ext-pdo_pgsql` +* `ext-pdo_sqlite` +* `ext-pdo_oracle` +* `ext-pdo_odbc` +* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe` +* `ext-sockets` +* `ext-soap` +* `sleep`/`usleep`/`time_sleep_until` +* `proc_open` +* `gethostbyname`/`shell_exec`/`exec` +* `fread`/`fopen`/`fsockopen`/`fwrite`/`flock` + + +## 🛠 Develop & Discussion + ++ __IDE Helper & API__: ++ __Twitter__: ++ __Discord__: ++ __中文社区__: + +## 💎 Awesome Swoole +Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including + +* Swoole-based frameworks and libraries. +* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii. +* Books, videos, and other learning materials about Swoole. +* Debugging, profiling, and testing tools for developing Swoole-based applications. +* Coroutine-friendly packages and libraries. +* Other Swoole related projects and resources. ## ✨ Event-based @@ -449,7 +555,7 @@ echo 'use ' . (microtime(true) - $s) . ' s'; ### Compiling requirements + Linux, OS X or Cygwin, WSL -+ PHP 7.2.0 or later (The higher the version, the better the performance.) ++ PHP 8.1.0 or later (The higher the version, the better the performance.) + GCC 4.8 or later ### 1. Install via PECL (beginners) @@ -460,10 +566,11 @@ pecl install swoole ### 2. Install from source (recommended) -Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or: +Please 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. +To clone the source code from git specify a tag: ```shell -git clone https://github.com/swoole/swoole-src.git && \ +git clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \ cd swoole-src ``` @@ -485,9 +592,7 @@ After compiling and installing to the system successfully, you have to add a new + `--enable-openssl` or `--with-openssl-dir=DIR` + `--enable-sockets` -+ `--enable-http2` + `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method) -+ `--enable-swoole-json` + `--enable-swoole-curl` ### Upgrade @@ -513,25 +618,6 @@ sudo make install Enable it by adding a new line `extension=swoole_async.so` to `php.ini`. -## 💎 Frameworks & Components -+ [**Hyperf**](https://github.com/hyperf/hyperf) is a coroutine framework that focuses on hyperspeed and flexibility, specifically used for build microservices or middlewares. -+ [**Swoft**](https://github.com/swoft-cloud/swoft) is a modern, high-performance AOP and coroutine PHP framework. -+ [**Easyswoole**](https://www.easyswoole.com) is a simple, high-performance PHP framework, based on Swoole, which makes using Swoole as easy as `echo "hello world"`. -+ [**MixPHP**](https://github.com/mix-php/mix) is a powerful single-threaded coroutine framework with a very small footprint, simple and elegant. -+ [**imi**](https://github.com/Yurunsoft/imi) is a high-performance coroutine application development framework based on PHP Swoole, which supports the development of HttpApi, WebSocket, TCP, UDP services. -+ [**Saber**](https://github.com/swlib/saber) is a human-friendly, high-performance HTTP client component that has almost everything you can imagine. -+ [**One**](https://github.com/lizhichao/one) is a minimalist, high-performance PHP framework that supports the [swoole | php-fpm] environment - -## 🛠 Develop & Discussion - -+ __中文文档__: -+ __Documentation__: -+ __IDE Helper & API__: -+ __Debug Tool__: -+ __中文社区__: -+ __Twitter__: -+ __Slack Group__: - ## 🍭 Benchmark + 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. diff --git a/SUPPORTED.md b/SUPPORTED.md deleted file mode 100644 index cd2d76ebef2..00000000000 --- a/SUPPORTED.md +++ /dev/null @@ -1,21 +0,0 @@ -Supported Versions ---- - -| Branch | Initialization | Active Support Until | Security Support Until | -| ---------------------------------------------------------- | -------------- | -------------------- | ---------------------- | -| [v4.4.x](https://github.com/swoole/swoole-src/tree/v4.4.x) | 2019-4-15 | 2020-4-30 | 2022-7-31 | -| ~~[v4.5.x](https://github.com/swoole/swoole-src/tree/v4.5.x)~~ [EOL] | 2019-12-20 | 2020-12-31 | 2021-3-31 | -| [v4.6.x](https://github.com/swoole/swoole-src/tree/master) | 2021-01-06 | 2022-01-31 | 2023-01-31 | - -| Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | -| ------------------- | ------------------------------------------------------------ | -| Security fixes only | A release that is supported for critical security issues only. Releases are only made on an as-needed basis. | - -Unsupported Branches ---- -> 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. - -- `v1.x` (2012-7-1 ~ 2018-05-14) -- `v2.x` (2016-12-30 ~ 2018-05-23) -- `v4.0.x`, `v4.1.x`, `v4.2.x` (2018-06-14 ~ 2019-02-02) -- `v4.3.x` (2019-2-7 ~ 2019-12-31) diff --git a/code_format.sh b/code_format.sh deleted file mode 100755 index dd655d99126..00000000000 --- a/code_format.sh +++ /dev/null @@ -1,35 +0,0 @@ -# core source file -clang-format -i src/core/*.cc -clang-format -i src/coroutine/*.cc -clang-format -i src/lock/*.cc -clang-format -i src/memory/*.cc -clang-format -i src/network/*.cc -clang-format -i src/os/*.cc -clang-format -i src/pipe/*.cc -clang-format -i src/protocol/*.cc -clang-format -i src/reactor/*.cc -clang-format -i src/server/*.cc -clang-format -i src/wrapper/*.cc -# core header file -clang-format -i include/*.h - -# ext source file -clang-format -i *.cc -clang-format -i *.h - -clang-format -i examples/cpp/*.cc - -# core-tests source file -clang-format -i core-tests/src/_lib/*.cpp -clang-format -i core-tests/src/client/*.cpp -clang-format -i core-tests/src/core/*.cpp -clang-format -i core-tests/src/coroutine/*.cpp -clang-format -i core-tests/src/lock/*.cpp -clang-format -i core-tests/src/memory/*.cpp -clang-format -i core-tests/src/network/*.cpp -clang-format -i core-tests/src/os/*.cpp -clang-format -i core-tests/src/process/*.cpp -clang-format -i core-tests/src/protocol/*.cpp -clang-format -i core-tests/src/reactor/*.cpp -clang-format -i core-tests/src/server/*.cpp -clang-format -i core-tests/src/main.cpp \ No newline at end of file diff --git a/code_stats.sh b/code_stats.sh deleted file mode 100755 index be5403e14c3..00000000000 --- a/code_stats.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -cloc . --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git diff --git a/codecov.yml b/codecov.yml index 58405861c1d..3aaaf7fa7e4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,8 @@ ignore: - - "src/core/error.cc" \ No newline at end of file + - "src/core/error.cc" + - "thirdparty/hiredis/*" + +coverage: + range: 60..80 + round: down + precision: 2 diff --git a/composer.json b/composer.json new file mode 100644 index 00000000000..f276cb92605 --- /dev/null +++ b/composer.json @@ -0,0 +1,74 @@ +{ + "name": "swoole/swoole", + "type": "php-ext", + "license": "Apache-2.0", + "description": "Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.", + "require": { + "php": ">= 8.1" + }, + "php-ext": { + "extension-name": "swoole", + "configure-options": [ + { + "name": "enable-sockets", + "description": "Enable sockets support" + }, + { + "name": "enable-openssl", + "description": "Enable openssl support" + }, + { + "name": "with-openssl-dir", + "description": "Include OpenSSL support (requires OpenSSL >= 1.0.2)", + "needs-value": true + }, + { + "name": "enable-mysqlnd", + "description": "Enable mysqlnd support" + }, + { + "name": "enable-swoole-curl", + "description": "Enable curl support" + }, + { + "name": "enable-cares", + "description": "Enable cares support" + }, + { + "name": "enable-brotli", + "description": "Enable brotli support" + }, + { + "name": "with-brotli-dir", + "description": "Include Brotli support", + "needs-value": true + }, + { + "name": "enable-swoole-pgsql", + "description": "Enable PostgreSQL database support" + }, + { + "name": "with-swoole-odbc", + "description": "Enable ODBC database support", + "needs-value": true + }, + { + "name": "with-swoole-oracle", + "description": "Enable Oracle database support", + "needs-value": true + }, + { + "name": "enable-swoole-sqlite", + "description": "Enable Sqlite database support" + }, + { + "name": "enable-swoole-thread", + "description": "Enable swoole thread support (need php zts support)" + }, + { + "name": "enable-iouring", + "description": "Enable iouring for file async support" + } + ] + } +} diff --git a/config.m4 b/config.m4 index d5e465933c1..82a8645d512 100644 --- a/config.m4 +++ b/config.m4 @@ -12,7 +12,7 @@ dnl | If you did not receive a copy of the Apache2.0 license and are unable| dnl | to obtain it through the world-wide-web, please send a note to | dnl | license@swoole.com so we can mail you a copy immediately. | dnl +----------------------------------------------------------------------+ -dnl | Author: Tianfeng Han | +dnl | Author: Tianfeng Han | dnl | Author: Twosee | dnl +----------------------------------------------------------------------+ @@ -36,11 +36,6 @@ PHP_ARG_ENABLE([openssl], [AS_HELP_STRING([--enable-openssl], [Use openssl])], [no], [no]) -PHP_ARG_ENABLE([http2], - [enable http2.0 support], - [AS_HELP_STRING([--enable-http2], - [Use http2.0])], [no], [no]) - PHP_ARG_ENABLE([swoole], [swoole support], [AS_HELP_STRING([--enable-swoole], @@ -50,21 +45,41 @@ PHP_ARG_ENABLE([mysqlnd], [enable mysqlnd support], [AS_HELP_STRING([--enable-mysqlnd], [Enable mysqlnd])], [no], [no]) - + PHP_ARG_ENABLE([cares], [enable c-ares support], [AS_HELP_STRING([--enable-cares], [Enable cares])], [no], [no]) +PHP_ARG_ENABLE([iouring], + [enable io-uring support], + [AS_HELP_STRING([--enable-iouring], + [Enable io-uring])], [no], [no]) + PHP_ARG_WITH([openssl_dir], [dir of openssl], [AS_HELP_STRING([[--with-openssl-dir[=DIR]]], [Include OpenSSL support (requires OpenSSL >= 1.0.2)])], [no], [no]) -PHP_ARG_WITH([jemalloc_dir], - [dir of jemalloc], - [AS_HELP_STRING([[--with-jemalloc-dir[=DIR]]], - [Include jemalloc support])], [no], [no]) +PHP_ARG_ENABLE([brotli], + [enable brotli support], + [AS_HELP_STRING([[--enable-brotli]], + [Use brotli])], [auto], [no]) + +PHP_ARG_WITH([brotli_dir], + [dir of brotli], + [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], + [Include Brotli support])], [no], [no]) + +PHP_ARG_ENABLE([zstd], + [enable zstd support], + [AS_HELP_STRING([[--enable-zstd]], + [Use zstd])], [no], [no]) + +PHP_ARG_WITH([nghttp2_dir], + [dir of nghttp2], + [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]], + [Include nghttp2 support])], [no], [no]) PHP_ARG_ENABLE([asan], [enable asan], @@ -81,21 +96,57 @@ PHP_ARG_ENABLE([swoole-dev], [AS_HELP_STRING([--enable-swoole-dev], [Enable developer flags])], [no], [no]) -PHP_ARG_ENABLE([swoole-json], - [whether to enable Swoole JSON build flags], - [AS_HELP_STRING([--enable-swoole-json], - [Enable JSON support])], [no], [no]) - PHP_ARG_ENABLE([swoole-curl], [whether to enable Swoole CURL build flags], [AS_HELP_STRING([--enable-swoole-curl], [Enable cURL support])], [no], [no]) +PHP_ARG_ENABLE([swoole-pgsql], + [whether to enable postgresql build flags], + [AS_HELP_STRING([--enable-swoole-pgsql], + [Enable postgresql support])], [no], [no]) + PHP_ARG_ENABLE([thread-context], [whether to enable thread context], [AS_HELP_STRING([--enable-thread-context], [Use thread context])], [no], [no]) +PHP_ARG_ENABLE([swoole-thread], + [whether to enable swoole thread support], + [AS_HELP_STRING([--enable-swoole-thread], + [Enable swoole thread support])], [no], [no]) + +PHP_ARG_ENABLE([swoole-coro-time], + [whether to enable coroutine execution time ], + [AS_HELP_STRING([--enable-swoole-coro-time], + [Calculating coroutine execution time])], [no], [no]) + +define([PDO_ODBC_HELP_TEXT],[[ + The include and lib dirs are looked for under 'dir'. The 'flavour' can be one + of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for + the flavour you have selected will be used. e.g.: --with-swoole-odbc=unixODBC + will check for unixODBC under /usr/local. You may attempt to use an otherwise + unsupported driver using the 'generic' flavour. The syntax for generic ODBC + support is: --with-swoole-odbc=generic,dir,libname,ldflags,cflags. When built as + 'shared' the extension filename is always pdo_odbc.so]]) + +PHP_ARG_WITH([swoole-odbc], + ["for ODBC v3 support for PDO"], + [AS_HELP_STRING([--with-swoole-odbc=flavour,dir], + ["PDO: Support for 'flavour' ODBC driver."]PDO_ODBC_HELP_TEXT)], [no], [no]) + +AC_DEFUN([PDO_ODBC_CHECK_HEADER],[ + AC_MSG_CHECKING([for $1 in $PDO_ODBC_INCDIR]) + if test -f "$PDO_ODBC_INCDIR/$1"; then + php_pdo_have_header=yes + AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) + AC_DEFUN([SWOOLE_HAVE_PHP_EXT], [ extname=$1 haveext=$[PHP_]translit($1,a-z_-,A-Z__) @@ -132,6 +183,7 @@ AC_DEFUN([AC_SWOOLE_CPU_AFFINITY], #include typedef cpuset_t cpu_set_t; #else + #define _GNU_SOURCE 1 #include #endif ]], [[ @@ -202,7 +254,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT], AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], [ AC_MSG_CHECKING([for valgrind]) - AC_LANG([C++]) + AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -213,12 +265,13 @@ AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], ],[ AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) ]) AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], [ AC_MSG_CHECKING([for valgrind]) - AC_LANG([C++]) + AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -229,40 +282,50 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], ],[ AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) ]) -AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ - dnl Check for struct cmsghdr - AC_CACHE_CHECK([for struct cmsghdr], ac_cv_cmsghdr, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include ]], [[struct cmsghdr s; s]])], [ac_cv_cmsghdr=yes], [ac_cv_cmsghdr=no]) +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_FUTEX], +[ + AC_MSG_CHECKING([for io_uring futex]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + ]], [[ + int op = IORING_OP_FUTEX_WAIT; + ]])],[ + AC_DEFINE([HAVE_IOURING_FUTEX], 1, [have io_uring futex?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) ]) +]) - if test "$ac_cv_cmsghdr" = yes; then - AC_DEFINE(HAVE_CMSGHDR,1,[Whether you have struct cmsghdr]) - fi +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_STATX], +[ + AC_MSG_CHECKING([for io_uring statx]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + #include + #include + ]], [[ + struct statx _statxbuf; + memset(&_statxbuf, 0, sizeof(_statxbuf)); + int op = IORING_OP_STATX; + ]])],[ + AC_DEFINE([HAVE_IOURING_STATX], 1, [have io_uring statx?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) +AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex if_indextoname]) AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h sys/sockio.h]) AC_DEFINE([HAVE_SOCKETS], 1, [ ]) - dnl Check for fied ss_family in sockaddr_storage (missing in AIX until 5.3) - AC_CACHE_CHECK([for field ss_family in struct sockaddr_storage], ac_cv_ss_family, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include -#include - ]], [[struct sockaddr_storage sa_store; sa_store.ss_family = AF_INET6;]])], - [ac_cv_ss_family=yes], [ac_cv_ss_family=no]) - ]) - - if test "$ac_cv_ss_family" = yes; then - AC_DEFINE(HAVE_SA_SS_FAMILY,1,[Whether you have sockaddr_storage.ss_family]) - fi - dnl Check for AI_V4MAPPED flag AC_CACHE_CHECK([if getaddrinfo supports AI_V4MAPPED],[ac_cv_gai_ai_v4mapped], [ @@ -301,19 +364,6 @@ AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ if test "$ac_cv_gai_ai_idn" = yes; then AC_DEFINE(HAVE_AI_IDN,1,[Whether you have AI_IDN]) fi - - AC_CACHE_CHECK([if gethostbyname2_r is supported],[ac_cv_gethostbyname2_r], - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include - ]], [[struct hostent s, *res; char ptr[256]; int err; gethostbyname2_r("example.com", AF_INET, &s, ptr, sizeof(ptr), &res, &err);]])], - [ac_cv_gethostbyname2_r=yes], [ac_cv_gethostbyname2_r=no]) - ]) - - if test "$ac_cv_gethostbyname2_r" = yes; then - AC_DEFINE(HAVE_GETHOSTBYNAME2_R,1,[Whether you have gethostbyname2_r]) - fi ]) AC_MSG_CHECKING([if compiling with clang]) @@ -327,7 +377,8 @@ AC_COMPILE_IFELSE([ ) AC_MSG_RESULT([$CLANG]) -AC_PROG_CC_C99 +dnl AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 yet necessary for <= 2.69. +m4_version_prereq([2.70],,[AC_PROG_CC_C99]) AC_CANONICAL_HOST @@ -346,9 +397,11 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, inotify_init, AC_DEFINE(HAVE_INOTIFY, 1, [have inotify])) AC_CHECK_LIB(c, malloc_trim, AC_DEFINE(HAVE_MALLOC_TRIM, 1, [have malloc_trim])) AC_CHECK_LIB(c, inotify_init1, AC_DEFINE(HAVE_INOTIFY_INIT1, 1, [have inotify_init1])) + AC_CHECK_LIB(c, gethostbyname2_r, AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [have gethostbyname2_r])) AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) + AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) @@ -356,9 +409,7 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(pthread, pthread_mutexattr_setpshared, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETPSHARED, 1, [have pthread_mutexattr_setpshared])) AC_CHECK_LIB(pthread, pthread_mutexattr_setrobust, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST, 1, [have pthread_mutexattr_setrobust])) AC_CHECK_LIB(pthread, pthread_mutex_consistent, AC_DEFINE(HAVE_PTHREAD_MUTEX_CONSISTENT, 1, [have pthread_mutex_consistent])) - AC_CHECK_LIB(pcre, pcre_compile, AC_DEFINE(HAVE_PCRE, 1, [have pcre])) - AC_CHECK_LIB(cares, ares_gethostbyname, AC_DEFINE(HAVE_CARES, 1, [have c-ares])) - + if test "$PHP_SWOOLE_DEV" = "yes"; then AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") AX_CHECK_COMPILE_FLAG(-Wignored-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wignored-qualifiers") @@ -394,34 +445,488 @@ if test "$PHP_SWOOLE" != "no"; then CXXFLAGS="-g -O0 -Wall $CXXFLAGS" fi - if test "$PHP_SWOOLE_JSON" = "yes"; then - AC_DEFINE(SW_USE_JSON, 1, [do we enable json decoder]) - fi - if test "$PHP_SWOOLE_CURL" = "yes"; then + PKG_CHECK_MODULES([CURL], [libcurl >= 7.56.0]) + PHP_EVAL_LIBLINE($CURL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CURL_CFLAGS) AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) fi + if test "$PHP_SWOOLE_CORO_TIME" = "yes"; then + AC_DEFINE(SW_CORO_TIME, 1, [do we enable to calculate coroutine execution time]) + fi + + dnl pgsql begin + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + dnl TODO macros below can be reused to find curl things + dnl prepare pkg-config + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + AC_MSG_CHECKING(for libpq) + if test "x${LIBPQ_LIBS+set}" = "xset" || test "x${LIBPQ_CFLAGS+set}" = "xset"; then + AC_MSG_RESULT([using LIBPQ_CFLAGS and LIBPQ_LIBS]) + elif test -x "$PKG_CONFIG" ; then + dnl find pkg using pkg-config cli tool + libpq_pkg_config_path="$PHP_SWOOLE_PGSQL/lib/pkgconfig" + if test "xyes" = "x$PHP_SWOOLE_PGSQL" ; then + libpq_pkg_config_path=/lib/pkgconfig + fi + if test "x" != "x$PKG_CONFIG_PATH"; then + libpq_pkg_config_path="$libpq_pkg_config_path:$PKG_CONFIG_PATH" + fi + + libpq_version_full=`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --modversion libpq` + AC_MSG_RESULT(${libpq_version_full}) + LIBPQ_CFLAGS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --cflags libpq`" + LIBPQ_LIBS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --libs libpq`" + fi + + _libpq_saved_cflags="$CFLAGS" + CFLAGS="$CFLAGS $LIBPQ_CFLAGS" + AC_CHECK_HEADER(libpq-fe.h, [], [ + dnl this is too long, wht so chaos? + cat >&2 <&2 </dev/null` + if test "$SWOOLE_PDO_OCI_TAIL1" = "a"; then + SWOOLE_PDO_OCI_TAIL1="tail -n1" + else + SWOOLE_PDO_OCI_TAIL1="tail -1" + fi + + AC_DEFUN([AC_PDO_OCI_VERSION],[ + AC_MSG_CHECKING([Oracle version]) + PDO_OCI_LCS_BASE=$PDO_OCI_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME + dnl Oracle 10g, 11g, 12c etc + PDO_OCI_LCS=`ls $PDO_OCI_LCS_BASE.*.1 2> /dev/null | $SWOOLE_PDO_OCI_TAIL1` + if test -f "$PDO_OCI_LCS"; then + dnl Oracle 10g, 11g 12c etc. The x.2 version libraries are named x.1 for + dnl drop in compatibility + PDO_OCI_VERSION=`echo $PDO_OCI_LCS | $SWOOLE_PDO_OCI_SED -e 's/.*\.\(.*\)\.1$/\1.1/'` + elif test -f $PDO_OCI_LCS_BASE.9.0; then + dnl There is no case for Oracle 9.2. Oracle 9.2 libraries have a 9.0 suffix + dnl for drop-in compatibility with Oracle 9.0 + PDO_OCI_VERSION=9.0 + else + AC_MSG_ERROR(Oracle libclntsh.$SHLIB_SUFFIX_NAME client library not found or its version is lower than 9) + fi + AC_MSG_RESULT($PDO_OCI_VERSION) + ]) + + AC_DEFUN([AC_PDO_OCI_CHECK_LIB_DIR],[ + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + ],[ + AC_MSG_RESULT([yes]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + ]) + + AC_MSG_CHECKING([OCI8 libraries dir]) + if test -d "$PDO_OCI_DIR/lib" && test ! -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + elif test ! -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + elif test -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR=$TMP_PDO_OCI_LIB_DIR + else + AC_MSG_ERROR([Oracle required OCI8 libraries not found]) + fi + AC_MSG_RESULT($PDO_OCI_LIB_DIR) + ]) + + PHP_ARG_WITH([swoole-oracle], + [whether to enable oracle build flags], + [AS_HELP_STRING([[--with-swoole-oracle[=DIR]]], + ["PDO: Oracle OCI support. DIR defaults to ${ORACLE_HOME}. Use + --with-swoole-oracle=instantclient,/path/to/instant/client/lib for an Oracle + Instant Client installation."])], [no], [no]) + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + AC_MSG_CHECKING([Oracle Install-Dir]) + if test "$PHP_SWOOLE_ORACLE" = "yes" || test -z "$PHP_SWOOLE_ORACLE"; then + PDO_OCI_DIR=$ORACLE_HOME + else + PDO_OCI_DIR=$PHP_SWOOLE_ORACLE + fi + AC_MSG_RESULT($PHP_SWOOLE_ORACLE) + + AC_MSG_CHECKING([if that is sane]) + if test -z "$PDO_OCI_DIR"; then + AC_MSG_ERROR([You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_HOME.]) + else + AC_MSG_RESULT([yes]) + fi + + if test "instantclient" = "`echo $PDO_OCI_DIR | cut -d, -f1`" ; then + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + PDO_OCI_CLIENT_DIR="client" + ],[ + AC_MSG_RESULT([yes]) + PDO_OCI_CLIENT_DIR="client64" + ]) + + PDO_OCI_LIB_DIR="`echo $PDO_OCI_DIR | cut -d, -f2`" + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + + AC_MSG_CHECKING([for oci.h]) + dnl Header directory for Instant Client SDK RPM install + OCISDKRPMINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib/oracle/\(.*\)/\('${PDO_OCI_CLIENT_DIR}'\)/lib[/]*$!\1/include/oracle/\2/\3!'` + + dnl Header directory for manual installation + OCISDKMANINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib[/]*$!\1/include!'` + + dnl Header directory for Instant Client SDK zip file install + OCISDKZIPINC=$PDO_OCI_LIB_DIR/sdk/include + + if test -f "$OCISDKRPMINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKRPMINC) + AC_MSG_RESULT($OCISDKRPMINC) + elif test -f "$OCISDKMANINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKMANINC) + AC_MSG_RESULT($OCISDKMANINC) + elif test -f "$OCISDKZIPINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKZIPINC) + AC_MSG_RESULT($OCISDKZIPINC) + else + AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install]) + fi + else + AC_PDO_OCI_CHECK_LIB_DIR($PDO_OCI_DIR) + + if test -d "$PDO_OCI_DIR/rdbms/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/public" + fi + if test -d "$PDO_OCI_DIR/rdbms/demo"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/demo) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/demo" + fi + if test -d "$PDO_OCI_DIR/network/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/network/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/network/public" + fi + if test -d "$PDO_OCI_DIR/plsql/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/plsql/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/plsql/public" + fi + if test -d "$PDO_OCI_DIR/include"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/include) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/include" + fi + + if test -f "$PDO_OCI_LIB_DIR/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_LIB_DIR/sysliblist`, SWOOLE_SHARED_LIBADD) + elif test -f "$PDO_OCI_DIR/rdbms/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_DIR/rdbms/lib/sysliblist`, SWOOLE_SHARED_LIBADD) + fi + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + fi + + case $PDO_OCI_VERSION in + 7.3|8.0|8.1) + AC_MSG_ERROR([Oracle client libraries < 9 are not supported]) + ;; + esac + + PHP_ADD_LIBRARY(clntsh, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBPATH($PDO_OCI_LIB_DIR, SWOOLE_SHARED_LIBADD) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvCreate, + [ + AC_DEFINE(HAVE_OCIENVCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate, + [ + AC_DEFINE(HAVE_OCIENVNLSCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Scrollable cursors? + PHP_CHECK_LIBRARY(clntsh, OCIStmtFetch2, + [ + AC_DEFINE(HAVE_OCISTMTFETCH2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Can handle bytes vs. characters? + PHP_CHECK_LIBRARY(clntsh, OCILobRead2, + [ + AC_DEFINE(HAVE_OCILOBREAD2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_OCI_INCLUDE" + PHP_CHECK_PDO_INCLUDES + AC_DEFINE_UNQUOTED(SWOOLE_PDO_OCI_CLIENT_VERSION, "$PDO_OCI_VERSION", [ ]) + AC_DEFINE(SW_USE_ORACLE, 1, [do we enable oracle coro support]) + fi + dnl SWOOLE_ORACLE stop + + dnl sqlite start + PHP_ARG_ENABLE([swoole-sqlite], + ["for sqlite 3 support for PDO"], + [AS_HELP_STRING([--enable-swoole-sqlite], + [PDO: sqlite 3 support.])], [no], [no]) + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + PHP_CHECK_PDO_INCLUDES + + PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.7]) + + PHP_EVAL_INCLINE($SQLITE_CFLAGS) + PHP_EVAL_LIBLINE($SQLITE_LIBS, SWOOLE_SHARED_LIBADD) + AC_DEFINE(HAVE_SW_PDO_SQLITELIB, 1, [Define to 1 if you have the pdo_sqlite extension enabled.]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_close_v2, [ + AC_DEFINE(HAVE_SW_SQLITE3_CLOSE_V2, 1, [have sqlite3_close_v2]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_column_table_name, [ + AC_DEFINE(HAVE_SW_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + AC_DEFINE(SW_USE_SQLITE, 1, [do we enable sqlite coro support]) + fi + dnl sqlite stop + AC_CHECK_LIB(z, gzgets, [ - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ - AC_CHECK_LIB(brotlidec, BrotliDecoderCreateInstance, [ - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) - PHP_ADD_LIBRARY(brotlienc, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(brotlidec, 1, SWOOLE_SHARED_LIBADD) - ]) - ]) + if test "$PHP_BROTLI_DIR" != "no"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + elif test "$PHP_BROTLI" = "yes"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc]) + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + elif test "$PHP_BROTLI" = "auto"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc], [found_brotlienc=yes], [found_brotlienc=no]) + if test "$found_brotlienc" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + fi + + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec], [found_brotlidec=yes], [found_brotlidec=no]) + if test "$found_brotlidec" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + fi + fi + + if test "$PHP_ZSTD" = "yes"; then + PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.4.0]) + AC_DEFINE(SW_HAVE_ZSTD, 1, [have zstd]) + PHP_EVAL_LIBLINE($ZSTD_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($ZSTD_CFLAGS) + fi PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) AC_ARG_ENABLE(debug, - [ --enable-debug, compile with debug symbols], + [ --enable-debug Compile with debug symbols], [PHP_DEBUG=$enableval], [PHP_DEBUG=0] ) @@ -441,6 +946,10 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_LOG_TRACE_OPEN, 1, [enable trace log]) fi + if test "$PHP_SWOOLE_THREAD" != "no"; then + AC_DEFINE(SW_THREAD, 1, [enable swoole thread support]) + fi + if test "$PHP_SOCKETS" = "yes"; then AC_MSG_CHECKING([for php_sockets.h]) @@ -461,10 +970,13 @@ if test "$PHP_SWOOLE" != "no"; then if test "$PHP_THREAD" = "yes"; then AC_DEFINE(SW_USE_THREAD, 1, [enable thread support]) fi - + if test "$PHP_CARES" = "yes"; then + PKG_CHECK_MODULES([CARES], [libcares]) + PHP_EVAL_LIBLINE($CARES_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CARES_CFLAGS) AC_DEFINE(SW_USE_CARES, 1, [do we enable c-ares support]) - PHP_ADD_LIBRARY(cares, 1, SWOOLE_SHARED_LIBADD) + AC_DEFINE(HAVE_CARES, 1, [have c-ares]) fi AC_SWOOLE_CPU_AFFINITY @@ -487,156 +999,109 @@ if test "$PHP_SWOOLE" != "no"; then CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - if test "$SW_OS" = "MAC"; then - AC_CHECK_LIB(c, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) + if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then + PKG_CHECK_MODULES([URING], [liburing >= 2.0]) + + AC_SWOOLE_HAVE_IOURING_STATX + AC_SWOOLE_HAVE_IOURING_FUTEX + + PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($URING_CFLAGS) + AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) + fi + + dnl Check should we link to librt + + if test "$SW_OS" = "LINUX"; then + GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') + AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) + + GLIBC_MAJOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 1) + GLIBC_MINOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 2) + + if test $GLIBC_MAJOR_VERSION -lt 2 || (test $GLIBC_MAJOR_VERSION -eq 2 && test $GLIBC_MINOR_VERSION -lt 17); then + OS_SHOULD_HAVE_LIBRT=1 + else + AC_MSG_NOTICE([link with -lrt (only for glibc version before 2.17)]) + OS_SHOULD_HAVE_LIBRT=0 + fi + elif test "$SW_OS" = "MAC"; then + OS_SHOULD_HAVE_LIBRT=0 else - AC_CHECK_LIB(rt, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) + AS_CASE([$host_os], + [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] + [OS_SHOULD_HAVE_LIBRT=1] + ) + fi + + if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then + AC_MSG_NOTICE([Librt is required on $host_os.]) + dnl Check for the existence of librt + AC_CHECK_LIB([rt], [clock_gettime], [], [ + AC_MSG_ERROR([We have to link to librt on your os, but librt not found.]) + ]) PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD) + else + AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.]) fi + if test "$SW_OS" = "LINUX"; then LDFLAGS="$LDFLAGS -z now" fi if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then if test "$PHP_OPENSSL_DIR" != "no"; then - AC_DEFINE(HAVE_OPENSSL, 1, [have openssl]) PHP_ADD_INCLUDE("${PHP_OPENSSL_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(ssl, "${PHP_OPENSSL_DIR}/${PHP_LIBDIR}") + + PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) else - AC_CHECK_LIB(ssl, SSL_connect, AC_DEFINE(HAVE_OPENSSL, 1, [have openssl])) + PKG_CHECK_MODULES([SSL], [libssl]) + PHP_EVAL_LIBLINE($SSL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($SSL_CFLAGS) + + PKG_CHECK_MODULES([CRYPTO], [libcrypto]) + PHP_EVAL_LIBLINE($CRYPTO_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CRYPTO_CFLAGS) fi AC_DEFINE(SW_USE_OPENSSL, 1, [enable openssl support]) - PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) fi - if test "$PHP_JEMALLOC_DIR" != "no"; then - AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) - PHP_ADD_INCLUDE("${PHP_JEMALLOC_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(jemalloc, "${PHP_JEMALLOC_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY(jemalloc, 1, SWOOLE_SHARED_LIBADD) + if test "$PHP_NGHTTP2_DIR" != "no"; then + PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY(nghttp2, 1, SWOOLE_SHARED_LIBADD) fi PHP_ADD_LIBRARY(pthread, 1, SWOOLE_SHARED_LIBADD) - if test "$PHP_HTTP2" = "yes"; then - AC_DEFINE(SW_USE_HTTP2, 1, [enable HTTP2 support]) - fi - if test "$PHP_MYSQLND" = "yes"; then PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) fi - swoole_source_file=" \ - ext-src/php_swoole.cc \ - ext-src/php_swoole_cxx.cc \ - ext-src/swoole_async_coro.cc \ - ext-src/swoole_atomic.cc \ - ext-src/swoole_channel_coro.cc \ - ext-src/swoole_client.cc \ - ext-src/swoole_client_coro.cc \ - ext-src/swoole_coroutine.cc \ - ext-src/swoole_coroutine_scheduler.cc \ - ext-src/swoole_coroutine_system.cc \ - ext-src/swoole_curl.cc \ - ext-src/swoole_event.cc \ - ext-src/swoole_http2_client_coro.cc \ - ext-src/swoole_http2_server.cc \ - ext-src/swoole_http_client_coro.cc \ - ext-src/swoole_http_request.cc \ - ext-src/swoole_http_response.cc \ - ext-src/swoole_http_server.cc \ - ext-src/swoole_http_server_coro.cc \ - ext-src/swoole_lock.cc \ - ext-src/swoole_mysql_coro.cc \ - ext-src/swoole_mysql_proto.cc \ - ext-src/swoole_process.cc \ - ext-src/swoole_process_pool.cc \ - ext-src/swoole_redis_coro.cc \ - ext-src/swoole_redis_server.cc \ - ext-src/swoole_runtime.cc \ - ext-src/swoole_server.cc \ - ext-src/swoole_server_port.cc \ - ext-src/swoole_socket_coro.cc \ - ext-src/swoole_table.cc \ - ext-src/swoole_timer.cc \ - ext-src/swoole_websocket_server.cc \ - src/core/base.cc \ - src/core/channel.cc \ - src/core/crc32.cc \ - src/core/error.cc \ - src/core/heap.cc \ - src/core/log.cc \ - src/core/string.cc \ - src/core/timer.cc \ - src/coroutine/base.cc \ - src/coroutine/channel.cc \ - src/coroutine/context.cc \ - src/coroutine/file_lock.cc \ - src/coroutine/hook.cc \ - src/coroutine/socket.cc \ - src/coroutine/system.cc \ - src/coroutine/thread_context.cc \ - src/lock/mutex.cc \ - src/lock/rw_lock.cc \ - src/lock/spin_lock.cc \ - src/memory/buffer.cc \ - src/memory/fixed_pool.cc \ - src/memory/global_memory.cc \ - src/memory/ring_buffer.cc \ - src/memory/shared_memory.cc \ - src/memory/table.cc \ - src/network/address.cc \ - src/network/client.cc \ - src/network/dns.cc \ - src/network/socket.cc \ - src/network/stream.cc \ - src/os/async_thread.cc \ - src/os/base.cc \ - src/os/file.cc \ - src/os/msg_queue.cc \ - src/os/pipe.cc \ - src/os/process_pool.cc \ - src/os/sendfile.cc \ - src/os/signal.cc \ - src/os/timer.cc \ - src/os/unix_socket.cc \ - src/os/wait.cc \ - src/protocol/base.cc \ - src/protocol/base64.cc \ - src/protocol/dtls.cc \ - src/protocol/http.cc \ - src/protocol/http2.cc \ - src/protocol/mime_type.cc \ - src/protocol/mqtt.cc \ - src/protocol/redis.cc \ - src/protocol/socks5.cc \ - src/protocol/ssl.cc \ - src/protocol/websocket.cc \ - src/reactor/base.cc \ - src/reactor/epoll.cc \ - src/reactor/kqueue.cc \ - src/reactor/poll.cc \ - src/reactor/select.cc \ - src/server/base.cc \ - src/server/manager.cc \ - src/server/master.cc \ - src/server/message_bus.cc \ - src/server/port.cc \ - src/server/process.cc \ - src/server/reactor_process.cc \ - src/server/reactor_thread.cc \ - src/server/static_handler.cc \ - src/server/task_worker.cc \ - src/server/worker.cc \ - src/wrapper/event.cc \ - src/wrapper/timer.cc" + AC_MSG_CHECKING([for sources]) + if test -f "$abs_srcdir/ext-src/php_swoole.cc"; then + swoole_source_dir=$abs_srcdir + elif test -f "ext-src/php_swoole.cc"; then + swoole_source_dir=$(pwd) + else + swoole_source_dir="ext/swoole" + fi + AC_MSG_RESULT([$swoole_source_dir]) + + ext_src_files=$(cd $swoole_source_dir && find ext-src/ -name *.cc) + lib_src_files=$(cd $swoole_source_dir && find src/ -name *.cc) + + swoole_source_file="${ext_src_files} ${lib_src_files}" swoole_source_file="$swoole_source_file \ thirdparty/php/curl/interface.cc \ thirdparty/php/curl/multi.cc \ + thirdparty/php84/curl/interface.cc \ + thirdparty/php84/curl/multi.cc \ thirdparty/php/sockets/multicast.cc \ thirdparty/php/sockets/sendrecvmsg.cc \ thirdparty/php/sockets/conversions.cc \ @@ -648,21 +1113,72 @@ if test "$PHP_SWOOLE" != "no"; then thirdparty/swoole_http_parser.c \ thirdparty/multipart_parser.c" - swoole_source_file="$swoole_source_file \ - thirdparty/hiredis/hiredis.c \ - thirdparty/hiredis/alloc.c \ - thirdparty/hiredis/net.c \ - thirdparty/hiredis/read.c \ - thirdparty/hiredis/sds.c" + if test "$PHP_NGHTTP2_DIR" = "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/nghttp2/nghttp2_hd.c \ + thirdparty/nghttp2/nghttp2_rcbuf.c \ + thirdparty/nghttp2/nghttp2_helper.c \ + thirdparty/nghttp2/nghttp2_buf.c \ + thirdparty/nghttp2/nghttp2_mem.c \ + thirdparty/nghttp2/nghttp2_hd_huffman.c \ + thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + fi - swoole_source_file="$swoole_source_file \ - thirdparty/nghttp2/nghttp2_hd.c \ - thirdparty/nghttp2/nghttp2_rcbuf.c \ - thirdparty/nghttp2/nghttp2_helper.c \ - thirdparty/nghttp2/nghttp2_buf.c \ - thirdparty/nghttp2/nghttp2_mem.c \ - thirdparty/nghttp2/nghttp2_hd_huffman.c \ - thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + dnl During static compilation, there is no php-config variable, + dnl but the php-version variable is always present and is not affected by the shell environment variables. + dnl During dynamic compilation, the php-config variable is always available, whereas the php-version variable is absent. + + if test -z "$PHP_CONFIG"; then + if test -z "$PHP_VERSION"; then + AC_MSG_ERROR([the PHP_VERSION variable must be defined]) + else + SW_PHP_VERSION=$PHP_VERSION + fi + else + SW_PHP_VERSION=`$PHP_CONFIG --version` + fi + + SW_PHP_VERSION_ID=`echo "${SW_PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` + + if test "$SW_PHP_VERSION_ID" = "82"; then + SW_PHP_THIRDPARTY_DIR="thirdparty/php81" + else + SW_PHP_THIRDPARTY_DIR="thirdparty/php${SW_PHP_VERSION_ID}" + fi + + AC_MSG_NOTICE([php version: $SW_PHP_VERSION, version_id: $SW_PHP_VERSION_ID, thirdparty_dir: $SW_PHP_THIRDPARTY_DIR]) + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + fi + fi + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" + fi + + if test "$PHP_SWOOLE_ODBC" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" + fi + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + fi + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" @@ -672,12 +1188,13 @@ if test "$PHP_SWOOLE" != "no"; then [amd64*], [SW_CPU="x86_64"], [x86*], [SW_CPU="x86"], [i?86*], [SW_CPU="x86"], - [arm*], [SW_CPU="arm"], - [aarch64*], [SW_CPU="arm64"], [arm64*], [SW_CPU="arm64"], + [aarch64*], [SW_CPU="arm64"], + [arm*], [SW_CPU="arm32"], [mips64*], [SW_CPU="mips64"], [mips*], [SW_CPU="mips32"], [riscv64*], [SW_CPU="riscv64"], + [loongarch64*], [SW_CPU="loongarch64"], [ SW_USE_ASM_CONTEXT="no" ] @@ -697,7 +1214,7 @@ if test "$PHP_SWOOLE" != "no"; then else SW_USE_ASM_CONTEXT="no" fi - elif test "$SW_CPU" = "arm"; then + elif test "$SW_CPU" = "arm32"; then if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="arm_aapcs_elf_gas.S" else @@ -739,13 +1256,19 @@ if test "$PHP_SWOOLE" != "no"; then else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "loongarch64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="loongarch64_sysv_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi else SW_USE_ASM_CONTEXT="no" fi if test "$PHP_THREAD_CONTEXT" != "no"; then - AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context]) - SW_USE_ASM_CONTEXT="no" + AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context]) + SW_USE_ASM_CONTEXT="no" fi if test "$SW_USE_ASM_CONTEXT" = "yes"; then @@ -755,12 +1278,14 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context]) fi - PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE", cxx) + EXTRA_CFLAGS="$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE" + + PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS", cxx) PHP_ADD_INCLUDE([$ext_srcdir]) PHP_ADD_INCLUDE([$ext_srcdir/include]) PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) - PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) + PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) AC_MSG_CHECKING([swoole coverage]) if test "$PHP_SWOOLE_COVERAGE" != "no"; then @@ -771,7 +1296,11 @@ if test "$PHP_SWOOLE" != "no"; then AC_MSG_RESULT([disabled]) fi - PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.h php_swoole.h include/*.h thirdparty/*.h thirdparty/hiredis/*.h]) + PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.h php_swoole.h \ + include/*.h \ + stubs/*.h \ + thirdparty/*.h \ + thirdparty/nghttp2/*.h]) PHP_REQUIRE_CXX() @@ -783,6 +1312,10 @@ if test "$PHP_SWOOLE" != "no"; then CXXFLAGS="$CXXFLAGS -std=c++11" fi + if test "$SW_CPU" = "arm"; then + PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD) + fi + PHP_ADD_BUILD_DIR($ext_builddir/ext-src) PHP_ADD_BUILD_DIR($ext_builddir/src/core) PHP_ADD_BUILD_DIR($ext_builddir/src/memory) @@ -796,9 +1329,31 @@ if test "$PHP_SWOOLE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl) + if test "$PHP_NGHTTP2_DIR" = "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) + fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql) + fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_odbc) + fi + if test "$PHP_SWOOLE_ORACLE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_oci) + fi + if test "$PHP_SWOOLE_SQLITE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_sqlite) + fi fi diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index b8f6478ed6a..807f5337cf6 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 2.8.12) project(core_tests) @@ -10,14 +10,14 @@ file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -set(core_tests_includes ./include/ ../thirdparty/hiredis ./deps/llhttp/include) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include /usr/local/include) set(core_tests_libraries) set(core_tests_link_directories /usr/local/lib) -list(APPEND core_tests_libraries pthread) +list(APPEND core_tests_libraries pthread gtest gtest_main) # find GTest -find_package(GTest) +find_package(GTest REQUIRED) if (!${GTEST_FOUND}) message(FATAL_ERROR "Not found GTest") endif() @@ -61,6 +61,10 @@ if (DEFINED enable_asan) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif() +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + # should execute before the add_executable command link_directories(${core_tests_link_directories}) diff --git a/core-tests/README-CN.md b/core-tests/README-CN.md new file mode 100644 index 00000000000..46d12ec9a0c --- /dev/null +++ b/core-tests/README-CN.md @@ -0,0 +1,50 @@ +**Swoole 核心单元测试** +=========== +## **1. 编译googletest** +swoole单元测试依赖于googletest,因此第一步我们需要编译googletest。 + +这里要求gcc和和g++编译器的版本要大于4.8.5。 +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +如果你已经安装了更高版本的gcc和g++编译器,但是在执行cmake . 的过程中还是发现如下输出。 +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +请执行下面的代码并重试。 +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. 编译swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. 拉取基础镜像** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. 运行swoole单元测试** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/README.md b/core-tests/README.md new file mode 100644 index 00000000000..2f3c6644b9b --- /dev/null +++ b/core-tests/README.md @@ -0,0 +1,54 @@ +**Swoole core unit testing** +=========== + +> All test files have the `.cpp` extension + +## **1. Compiling googletest** +Since swoole core unit testing rely on googletest, we need compile googletest at first. + +gcc compiler version > 4.8.5 and gcc-c++ compiler version > 4.8.5. +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +The output still contains the following messages after compiling higher version gcc and gcc-c++. + +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +Please execute following code and retry. +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. How to compile swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. Pulling images for testing** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. Run swoole core testing** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/docker-compose.yml b/core-tests/docker-compose.yml new file mode 100755 index 00000000000..4188b9de5f6 --- /dev/null +++ b/core-tests/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' +services: + httpbin: + container_name: "httpbin" + image: "kennethreitz/httpbin" + tinyproxy: + container_name: "tinyproxy" + image: "vimagick/tinyproxy" + socks5: + container_name: "socks5" + image: "xkuma/socks5" + ports: + - "8080:1080" + environment: + - PROXY_USER=user + - PROXY_PASSWORD=password + - PROXY_SERVER=0.0.0.0:1080 + diff --git a/core-tests/fuzz/cases/req1.bin b/core-tests/fuzz/cases/req1.bin new file mode 100644 index 00000000000..fc9b786372c --- /dev/null +++ b/core-tests/fuzz/cases/req1.bin @@ -0,0 +1,19 @@ +POST / HTTP/1.1 +Host: 127.0.0.1:35347 +Accept: */* +Content-Length: 11199 +Content-Type: multipart/form-data; boundary=------------------------18610972117ffaf2 + +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="test" + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="hello" + +ibOnkVsuKvLnzrTeS81ueAmswSWiUUvcOtHAamPBw2NIgEM3286PmK0lvCcPDnsYQ0lm8/emwynbcngwmUJ5983lmEBj49th5ED73eFUGHSLQzTY1nAaD54f +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="world" + +NDdKoaAEmCjD+MFAo1tOBTJ0yPTWG2amx0f8wI/PJf/sIB+XAxRmdftsyd48o47Lt8U8pgIxBd4xcUsLGVomZZT13eIpsCHnPjSdgAr8vSBrxFciyHBIzkhFX8IxTBFs+YqMqiH5/YWx/TQnxoqYWcqa86RShKne2JmF6+JXZUkB6O3I21X1cioUmdK9d3pOBWGUviL9oY99FA0fVAC9qH01hyO2AQyh3hjhvpq1uPYhjnCwetQlpYExhxPC2Q1gJ9edA8pmccWFSRlS079wq8l1ikkQqNnLvzp15pzNmUbvDGXAywsQrpJco3G9b8gwljHGUPpctxBB8DyO1LfavJ8Zusa9rSA4NAA3NzZpZ5bbKy5rYFQASuFXd7orlXdoPkyZ7ERSqgIT6y8Y3pnPrzAs8LLEmGuWbjN6ME7PtUuT87f10GtobckOUqYJktX9fmD4lxgJT0uUam++HG1j3ztFzyaY1pCREYmKewy2Q5FTgYQxKNzN4WdRmz+X8k2upRFDEr22Gm1wUEct2vOZDKhp0XBtV20uGKg0kIb4OM8RU5b/vymTss5+eHX5G/zy09+fXyzs7RpUls/yavSiZJT6iYXciBINqfYcmG6f3arBdP3sgR59UhLnWn3wfNeKyYshiy1HcKDvOEZea/A81XSULo4iVmlYghACFrwvkyHU6oaV/chWID0IsNWtylIo5OaDFDuZLGm3YzQ58ew21dQZ+Nifl2QydeGBO1siZt9mbcIAXqIylvP3iT1CVab7PCiGPXNtYntLa7581Zceg73i4g1ZzuaWULUqkF5WPLWroTCWzDl+/2WsU3+WAIOON3dRTz/DirZG7e0UxmaseAT+xoDyVMIZK0fuaoc3s0ozivKE4bItfYMMhkyRQpDIjzTKB3YHBCH9aWnVcxLRnVF6qt6hO/bZfh/isskDbiCoBD8YcSPWgN/qLYDBOJFy8us9TdiWPZdBebdHmqR889s4Mow3TYcrbmaVmnwwPqSWgsfhKiLoaK2MKWgSxVlGGVQuB8Odw/YrAvFAEANKNv0D92pUfWBY8Rolh6MMECiYTuHLeGBRENx/1TWaZ1ASP4hfp/fP0ylTSimfxexOEHKDJGlQdsSgQNt7uXYlFuwhRVu78FacQcLhSDMAJT9oXmmy9/BX4x8F68IkV9A2XYEfa4G6KzM4reMyccXIM2GnBKkoQgfwcPNuRwo/riyqQyMbf03MINJ/IwwmI1lnXRL6Uzmn3zUV9aurDrhRTrVfqAhPqEJLrqsPduI2DXQWvBXNiWQctK5pTCWnV9qj9qy25CBggvP8c749mlPPhTmrFt2Nk2QeS7t+oiq/E+G1ir0daEk+3M3oqEVRMbcnkftu+d2aMPuBSDwt6l7nV+GCQ89DXp20enwbeUU/JVLRGYkqpj+17Tw/8YxxYTTkVbNNotv8tOKqmwyN0XzRffQ03aVbNhjkUgESQahE05swWxRXrNmiUpIBFT/yMSSoqGl5aTbDtBjNPSpX7oSCCBNdoT3MnVdPCbPAMW4PxenhPUKWLLOVtFztMnFsl4/IIMtUjkBbt3cONJCIsNuXzSvfsEso9CYjH9VUWyDcZj614Ov8KIEYJZkJsF1nlxCJyChQedCDzLZNCR5sJY/R9dv5A88ixB1GhRSd5qjWpjnXTAm8YLVtDWzH9v0ACVNsq4zYYUQioJk6ck13meDgBEu4etqqyeVUdbJAkHo2R4MAo4uJ2/sMQL5xniZ5WRYTBP4JFJxkz7qNIzn8n+7XxuotnfvqShCuUC/Xt/SZFHD2INMBG2u4OLlARBWj7TPkLsQwczaGyXC9j85mbXvOpxbqlBAtADQlIOfqaeROahEkNKYnmCb09PF1Sss3l4BKnYaVShO+pZ7MnJSR0Mpx6R5uV0dGW/VXmeOSrSCouDkunAnX1jMEEje35szpDkqgMaYqlaoOkMRrhCZwL7YhQjSBe1GnFDHGvQxX8i3AnXfgzA+9AAiITOv7k0h/PxgGNUHlaBkWyaGIi3pS0Dz/4yGmP3BYr0H/qHpeXte1kd/s8JRxyILGDgC51Y+ExYvjZL63PsmmPNfHyY7LDTX/UfTweISeF7q1b8HuoQuwxvVmq6l/C+/ewDXVeort1tbDED+h1dCDqOEBXGupZu2uKHfWICgdLOHzPEnzyNsykqCAAlKTtFp6VAwLo3b3xWFSvhXfk4Yi/23+rZc3eL2JFwaHcULVkCr6AzOwriALLBBU2RT2qj1cAwqWNQQUbH7tHViuBxBT8/B9MjcU9HQHbdKSvEocs/4fqgMJfvj2YD4O7qGzLaBghkfKS1HAOyt3MNf5q4k7lQndHi0U8cAYMo6aAuxYOccxOzobU2uKiyZUx0WpH3ZE+Ge8W7Hc7bMSoH5ISNKTti8tIzNmt+CyIRPVzSjiodhaUr82ErCKq3UJTe3Kr+JAWkthB5BtcZWFVqMT7yb6cwtzoOiJKc+WaPX0rnwiHLJTQEhOGR/4Jh1YI39M8kpZG0HPPJbiK3bZuDgVyRgLsGm17vBpTv+dp93Kp3wquuATyGloMu7/GnjSrXzTOcVdkW0F5qMI+QSG0eK3130ndIw9Qt+Ir/UtYvqcTDCdrX5LJrJ3VLKSGqr5MPc8uHHhjZvWUeUtR8Dt7b9Qy8M7UZ+fWcRzVHMnqFzxbsOTkI9Ck1jlBe4p/Qks8EZ74zmc3SL2ZH672U9CQXhOLzyd/E435m60Pz5yqXjq/ZYdTl566IEoOA1YoqbbA+A2Co11Xjp3P2Mj3ns82ubjp9vkIkJboe9O4WghDbcPSWyiyioIUhp7mSrhgrZn8khgY0akKuRplrD58dhH0f+QCbk4/ovPjKLFwoiksfsvfQrO/5wSLAKQA+5rycB4JQj4gkDtKxW5/I2Mf0y/Js6gxAFMqCcRykc9Gm7WGRMmRDefluPLlajxOdj6jbA20RH23ydw4E5a0cRPYiZXlssEbrzu7qsdfisG4jmLoApu1PyokcshUWzkvJnevFg3LIgW9Xl3hKLyGLXQQR8f+ngVbP/rBkhY7R3lramIo7npGsCKQlv41/4Z6Vk+JTKd1mfvXq6mYJeeYHCAFw+BP8KcXFRav3J0XJccb69wuziZtu/h3KCOqL7q9mN0Y7ftOR+l+hJ+k+v/T0fbvHkwlBsI/I15f5r8S6KK3pnBcL14RD3dluZlr9GuR9hJtGc6GKmkV+QqPph7tPvSqtOaYI4CYrbHegBcWOirj7BEF0qGuNV6hYOxkZa6cSQP2p2rPTvzv3L2usPbCLDlgN7Yu+RJ0icpFCpkZE0V6w0sbr0ZBvYO8eaFu8TVwT84cXD2A8XSSCMm5w5HCQpIThfQzvW+xWTW7nO5y9LEPUQbh+fZMs+KABuEM5zIXyavqMdSumlWyX51FhAqhb4ys5LqQSrKM0u+qxUcweXOt2lsK1WgibFVIhuPzuCYo+hVFHNXQpxjpvX89N4Oxjg3jb3OWHOUqZsGU23aWLpOP8Vpmh4RxYZE6k07C16DZwHbe86tbSLrt6ezzjcmwZbtGwHd8iTi2sLjVZGplYENTw7Mglgr33ImoOD1MRjfWzTr2xwsfOzR+emSOnL3PBB1Xh6usXsyop/yeJQfJN4J+epduKW/2V6Egzc7t4Klf3wBtjysoFnTHiQ9u9IY81F0YjfHWUcAETiAZoGkl1LNCxRxa5a7zKHapEzu/xslNjnsXtNI7bghsUix3biyHDjU+eEBsO968R7haIoe3l/qF+/15EJKB0zCbiz/+muAU9/Q6Y6NZFWmKVz4TibFS7GdxMmaB5kM2Z3fEkOv27UqUk0p9J3n0lD9y14Zw+C4zyd15Ue2WVZVVAqwysjqAIPMfhDaV2f3Tvt6H6Ft8zolb1pjIl/EH09jUW2cRg7B3Q97qGFA0NVJ9CKDTbCwETEBiqnxiDrvHc2Pa4W19ZCIR3Ne3LorlaXLjMGSEmOmWBB10YH58FyIupUCCOLsK5PcBQoD+KoeVXkF4IhZJtajdlzpH4AmKP9MfxTZvQZO0vtixzYDyJ3PlNBa8zDKvJoJ01PIqZ1Z+AVo0pzesQ42Hz8Ch1V8wouwFN97Ah8QZzPJdR+kUcuBbqJ6u8WWZM3ippzpVdjQg5QwzjDxWxmgqgnTFXyW00yqNPQQDxi0bCXRD8x2f8P0Weh+NeWwajz4lXY310FfqWsCP07UsnsYxIk3vG1n5U37pJJZiJb3m07zH/HTwELeobIzYs2292tqW6yDSH8IZEHyBnvdtcIJ9ZrykG3NLjWByTA4w0j+VaiamfHmUIzY13q/PxCJxZUn8VnlZFJHXrI7iTO+FWKD39EZsKflsqslJZfFfVGGaJlPehq4nkiV4/YYP4Yi92DIkjCHiXXO4BlxWJbX+nPX1NF2B4ImHOThZM5Qr1yjv0oGKtxIx0zGuBLYiOWMTPGGLV4O+DBOkPm2XK3BUfoHjEIdAVprwqWK+4iah8XWmOrWuyk41OlvAd51h3LZRvRnkw+skhB+GOYDeQHKcmtSH645Hl2SU/abghLs8owaCPGorTDXNIBxbhoAFCRXVl5hMzA0hXnqWzWHHNpq3BKXGRCSovuYEjPlXM4KRR6n+cNzhWqXdpCkaNpXz8sy6rw43BM98Tf6djTdYR6QN+YYSC4/CD+ddxtmFI0l/tsNb5VU8fntbDQx5PS4Y7J37PbKvKS1gNCQo0Rlyc3GteFTKl/eg6nJMed86KaUYgO7I2XL8zzQ7C8ssgh6z07U9TyIHVw/ld80/B6UjwJdzu+o2izrQtOotgO1kZ2R6hhP5gA6z23UQ8RcOLPmOcHbq25kNGTsLmMLM6beFUQfn+W3TsWi0Nz5SLm+qOCbc/LEPNV0Y1ZcS1XwLHpr/feeflmOjc02/NCJrOxtRh4u/xPQtNQ90nGGXnajzaHY8Cmtl1bSBQ5ldfBevc1F0jGQeXQh26wwB7O/E6ypDyQw3glxzn2whATTBUGRNb5aM1b8QG7HwGDapucIGlc7EvGqnIOHKWZ7yYhmfZzAgW45pdtOybKQrzmrwIjngD/9z2a4BduwJwR40kS8n3am0Ao7UcLSv1LWL1Yn4rn8TiGhsBMOuGtepECOABBhqkB8Z0YXxgf0tUytSapuFX2NV45QXocoxd4xoLcyNeq1dWyTdlgqOzkvdsjUlMDyccmHUC0uQGaVnt7tV2D9lOZW0YgGcHFJN/MfhnVuoYLyiX+tJDsXXOvA5wMuU+jawoJ4a1MTiUBVLzS5W1iyO56od06nJhmM5ld0coIGW9bal//LSM2jG3hqeHjggy+QoDyCO+3gCYGAP7spmyb+UA0bjLnUROEKp843kx87iRnFb31dzX3PhvYEO5mRFnbAFpsxp+w99Z1Aspc0cJn0+4hTNTZ3ffRWmNSfhKkuitqeIUSLfIyZIQ5qbgeisH0bl783wm4f/1NKB/x0fWbrs3JOag+7BN3YJeihEx+YG8SQlyrNiZ8eBV8WLPl6BbML8ap5fxEwghKQkBVBnPNkRRxVSvT1bQetqWgtJHd0GpTyLp9oyMuOeSAobxk+dV0c+BXxD1WIuy/2pa7lOEiYTTVjlsXVI2QS6maV3dxf1T9gZHFaLPBKnHajQErCD71zpEHiByCRUQ0oO9bMvCpRyi8SYZX4mxFiT2KenJpqWHc6SPGpz1fq/RzzSAf3hhgRSYGZjhg+MLDIh3QhgB2oxE7aoNR5Wi2BpkPlh3cpg+KJXNjKs5DX6csBu1Ooeprc9jclolszA3jdFiNCI3vcCfLkHiZ0nmtAin9UzinLss/M2Dfzt6CZKM5s0JrNfpOxMIV+nOGR8YU0+BlkEych3vFqtQ3ETKpVNCesMGzYIAbsn4lehYv97lbneHJP6/zMlenEqF4etlaE/OkORz2LVcmPtfMYdetcWK+xZN/syzSYvYfmNTGJNppvhuKHnviAaL3TBx3PlTAFm3waadzWBitNzSVjdqtnNmgiOWaHYK6j3LosuigFnoqb0XtxP4zXMcruF/ikg1tFa4R0BSPXXikibnbqU7odFPT0jfY4KQn37rYu9U8iYqfmni7l/fu/MNH9qf7e/AQaKWL9cJATn48iIqLtT1OX4SEuPJUG1gYaQgZx5lVAQIKjI64hIq36c7syBGP1rlzmy7NKEbuE3kvQV0qOm2VUTA6u2lRxBTZfJu0VVJViVRdY1Jxw9aX/SFR1A2peGTXv4L7IdnGdRS971EkroEm/LiYibySrxxKhIMgr34jKa1p7mgjuGlPk7mvGkY079zlWsPr0UypSTQeMMThqNNZiycPQBjuMEkj2TxF11+5X1V+Xg/Bef/o9hYR0cOFh+zIjyd5O+Wj+dSfhEiBlFX5mdR+Av+00Ryvs0ePrDAOs2gfBGNqWpROMMwILTc6e4VgYn6+f4g8DYJzuS8UnlvIq1aH9W/UB+x9gH7tDogA9rIHn/XRSNiuJJA7Sqr2O1aB+iA/AokQFwISfi9Sjf57ih844QES0G9yDgTOCiylpkrR/FYtZcB2LlocbQvydmZwxoXpnHER8x/SW3YG85i6e1E2utK0MWN+WQs3QrHtttdfnp/j7QjKJpsLT2dS24MCgj7CDNJcY0SXyDWs4rmVj50zvzZhs3W+jNrvDIsh3FRGSu/fXiezJWIb4m3gevc8lomm5hzar1eBKSnNm5yZDU81HBKtxeDjmGLQ7jmxaqLb3+DvIs4v9HWYqs0uZ2CU1MA79FCAnX1LVJKzYROxAvhQPsukZ+tk38lClzkPryN7HGHR0xF0+UP9wVX9hK5Ss1CX+2lGTU0XkE8kufXTYh1R/Xque9AORgwT9IaumMnlPC9K1i64txCbHHmRgYtNcnwlbVMYy7UFH1Bc7IqYKQnu2l6cNoococsfwWJ6ciAJ+w8hMs2/ILnFvfvQunYVdEqu35GLlTZVF8O/+j9VydwwwzpEynZG6AzoKyP8GlPhQ8V/CcYXVBkEFMq4qUgvHhjoHRjuUom0IAx6InT4H8dG36UvslmzL2cNwP0NEDMu59lEGBAaa7RCjvR7frUMjjEXkcnXZH3KDPy/dwN5UX7FyghMdM4GxfSy7p79+ccJhjtTPoXrcVsMJdEntXhznJoSt51r3ZgeJNpwtrK/5d1jlLd5VGQDzH6UzH2gYxllNLjjoxaMNT30eGJ0nzaVWzn0X2JwLug1ZMOFPqyMDfrl94LzuxNWOUCw9cJ9pbqVaPmIzFM7o88O7BYJ5M90vgYqQvaTz0a2poECEoah/z2bxYrmDSIYiWnJ5TDFy3sOV2OHn8LLMS5JTV1bJh1uaht9pbjJjj1Rp9FetVUwXixiDNbSYPFUhkl3SXFeYTf5YWEpp6sVbMSKatWTUenQ4qESuZN+lKDSrOeIN1PheQkLwEAgIJYvMQkB23khDTqvMnVHahNU4OKZn+Xs+iyWa42/K2neqT+D4IOTAfUUXX0HGQJI0+8KE5NZrEMB+bB7mKRTNzW7Rg1aRmrgZoLgnb3hIft5VMrgebQSQ5UY0YwTNXg4fko+TlKoyEbO8PrYpawQ0vJOMt3T7lELcG+mWqHla7ba0o997l6KGrKcDT/cEe4z/hU5AQi391v+WfwSjxaMQewxCwh93+rUqqMXCWjVAi8SoxH514jVlMz1ukEXPMxiiwxb4+feME5aYN3pqdoPEccJZzEQpYQy9z02hLdTLMnrT3But8n1JjoT4AwTqBmzHlxPMSoWJvXKQ3nkUVb6BKhp3EM+kOd1kSw8HsRG/aWeZtTdm+CEOqX7YQYm47QKNOb3xS3dhzv4JsDvM/kkkfwYN/h/hOjbSZXUwdgvFtVznSu//3I5rxg6g6kDew1H8Y4Ax0yMhPSwycXtHqfbBptXOFfhvoJoYiz1w0nw5DhjFlLqMjBvj0mbWWpzIikpbl4cSpmmX2UP/HTfsf5BEull4YmY6/wNIc8jRlJbaENYf30De9ENhBjQRPDsjSXyeJb/2sa0Izb00G+bZtF63iRtPRgiBr2rJd1BDKOYSZcAMbB56o1dUwOc755Nhww/FGU9do3vpbFKgaFME8GXngdguC7+fh8DaFwe0B3J0iAhSQ9wX9VrU+XWsUCrK6Zlo2/33i0+5bbzprIhVBbnw78N1efYLOmEEkZvnRVXMyXmTapgDRmOVSuaK3vH5RKhFl28oUd4EQNALQ9r3vtAVr9sLkL55neaBAbGJ1HJeLATcgr5IcXkg3oI8mP2qDrWjD3NtQfezYej7uYslASEHMWeZ4EwyJDAVYaV1y6XPv10I6AFzT1iTwEnG614Ky9goFJDimDcIpnaDxZIe0A81kNF20y5ia7M3ycJK/G5QwvVxvy3qh4tDQKBn7GBbjFXmeVRasZtGiMgcyn4DpO6vABa3OMc+BAE8lekI8qdEDxo2c5ef09y63gL8TELxHfMEHoqUfDebn1miYLGVax5yqkvKbGME67SBJ2Y2GvFsfckeOU59GfzuVIpimjN+mqHy7FVryZVmdxSp4Klqdoq50ef0GstffYrw7Hb5HKRL/Hnjssxs+MqI0+b1M6DABP8WGsmFyU+7aToPMZGDmbEqr2K7/rCCrGwyYAcYOLjrT4sgGLeiNE9omcgYLPRYX0IV2QyKb7TmxCLQfB/J5Ea0MwIjqQZGZNzsCM8ox1TR+P5n/TQM+H+zk3miFzsbWRz6YYcLCKrGIr/jwXH++fsDl7bPWOakRR7xVnJs4/h260OvB9Q/xf/0QdeNMZK3XlK7n/gnFUsWFJCYISyLmVnl1qglxu4ohqS9r16Yra4HcqozrJfP6BWh/URq8lK8a1ssw7i3erS8umy4PZcrlI7H6YArt1v3K7tp3eQWhU0AngaQ6tguLoWTBzk46qBC4FV04suKlw0Prz0rOd0RM5g3ozm1NbbFvjUFBdRJDLBDaLpJRJajS6HpFZpNPaC7wQxBvOX1uMjuqfFB55VYT9sTOhIG1Rh5E8MiLulPFpotx6AkwgjnrRVhnd/plrSn8JRIXkXV+h4T69R7k+xHvOq6cJUwOlxyQXlgmM3M8UgYMSTRjjHhVPKF8ex3ikm2GPqEpsaaDn0RdvHAYja8333e2Ug8Q/1umXGi2XaOE1pIIWDF7Qry78tpAn1Mi2Xn3Ylo1AJXRRrj9AeJCMBNikTXEDvIwK5E+/luwOELnE1YslkQrZGPzAEoM+5GsqHZOpvI6F3dykob9wHIwrO6TShpcOlAc6LPqVmXYLXLMgnHCmE6/Cbl9000A6sXRmfNxIk/3+Eo3eY2WqW+YVt9XSWYQ56+/NV2HHVSMsGObNFZEGrUGyzc5L5+OrQVG0dVe/zGE5e0hERk0qj0l3JWapFIbzfm5Pw+qtg4IC6ylw0qiLma5XiDFQoxLMyCMXGEb0PbAdx02wZpxGK5LPsJf0falMrQOO6NSvDNb6+SBNvv2PoT5n1Vf9i7k8bwQ8ZR0FK34fA2WtlktA9ngh72lQcrxH+3wJ3sHUrqd1c0cgWNvEtmtRBS0dC7AR6Eveh/X4kHLQadOQDga8SD+WXyNPnlAzPoHmYU9zzJ9cM+9AXtKQ95kbm4iJFxu2i5ajv8LMgECo8CnnVt8ftW8FPhnYDeYtDSVY4A+aZd+Wmsr+SLzeFs8/R28VbUkgsL36/nLoGS+0s+SG+OSFWKtYSncPitI87m22Hsz4ZY+1Jynf+FbMoDrP7uDf5UGCaZIynsMq8rwaZzzQWNPK7aP1Za/yKRr2PhOTHHXFoQuwPlNPx1FO0UHKquzNirkhjRCApk+sHFsY9mkUahC33aXnQ08YEITCloGZQb+FIL5cE7NhqDa+UvDyWOBLQ6zZeHwQXrs3NY6RgnQ5mCJiIXSA45SJ1gEnlYvnGX9bj+z5et0QX0wflcQayV/B+sC48M59QKesGGAPhsH5wCRXAj7GsCQuAJjn5l8M8zx95dQoQf3wfltMOSh+mE6jy8q4ukof7dvlOeOD9unPKx56BSKFeX59T3pEM9gYMR9EN4IN7U5XGWbEi+6EfcTfUxgfNboFPykakTzku3uUABUpo3/8UMAsydGVg1cudDrBF+RnZT2vgK5i/1w5O9FPZvonXUq+5nHRJVUYn33GyccNkjrs+Y+yt08+QVDy+6wsX1SPOtEUaE59cpEdsjhbguOTn7/Kb6IbfeCnVChk6sBTLsvh7D4LUOQtbkzxhz+XCGFeRhvp4l3pBwmP8dv4PDROrAqp5UBIhKaJcZvvAhSwY2cbo+CNUYCcwrjWqjOwyEmedd2kCJp4s2b9bewldPHan5WX6AMecl/4NXp1sQrHPjO9en0Xz5n92qKpdZQC8p2AdSlASNhAAKBu6nBzlMbzoyex/vlJcWtAzMyleHdvHOHpFF0z0SF4kmkpGMz/WKxpH2cagE973ExU1ClJi/BIcjP+p203Z/YBTkZCFhy2GH8DK4jXWvPJIfkG5m5Z1xbrkNwJIfUfd90ubiYgbhxawEbKRjhFDtl7PV1wA8fHKOxCY6I0DN0mqxIDYgxc0FVE85+NSYR8z5ucjFCHQmCoSwWohznWvIANmVgDt5tu6HeCirL02uGNgmvpStMxosgYKU4Xt84XstQm19eQnJXTiceTogNzh3X3UWxELUnc46UC9NiMOZrOq/vKRgRUvW1JhwBhpmYOOyOdn8EqsJyAsjgFWNZE3gwi3RIaA6MIjkO6yCtJPs2g== +--------------------------18610972117ffaf2-- diff --git a/core-tests/fuzz/cases/req2.bin b/core-tests/fuzz/cases/req2.bin new file mode 100644 index 00000000000..7c0015fa2b0 Binary files /dev/null and b/core-tests/fuzz/cases/req2.bin differ diff --git a/core-tests/fuzz/project.json b/core-tests/fuzz/project.json new file mode 100644 index 00000000000..161f3f2dbf1 --- /dev/null +++ b/core-tests/fuzz/project.json @@ -0,0 +1,16 @@ +{ + "project": { + "name": "fuzz", + "type": "bin" + }, + "build": { + "ldflags": "", + "cxxflags": "", + "cflags": "", + "c_std": "", + "cxx_std": "" + }, + "install": { + "target": "" + } +} \ No newline at end of file diff --git a/core-tests/fuzz/src/main.cpp b/core-tests/fuzz/src/main.cpp new file mode 100644 index 00000000000..d945a40e675 --- /dev/null +++ b/core-tests/fuzz/src/main.cpp @@ -0,0 +1,48 @@ +#include "phpx_embed.h" +#include +#include +#include +#include + +using namespace php; +using namespace std; + +int main(int argc, char * argv[]) +{ + VM vm(argc, argv); + cout << "hello world" << endl; + + char buf[8192]; + ssize_t n; + + int fd = 0; + if (argc > 0) { + fd = open(argv[1], O_RDONLY); + } + + n = read(fd, buf, 8192); + if (n < 0) { + fprintf(stderr, "failed to read data\n"); + return 1; + } + + auto req_var = exec("Swoole\\Http\\Request::create"); + + var_dump(req_var); + + if (!req_var.isObject()) { + fprintf(stderr, "cannot create object of Swoole\\Http\\Request\n"); + return 2; + } + + Variant data(buf, n); + + auto req = Object(req_var); + auto retval = req.exec("parse", data); + + printf("retval=%ld", retval.toInt()); + + var_dump(req); + + return 0; +} diff --git a/core-tests/include/test_core.h b/core-tests/include/test_core.h index c56deb9ee55..21feb2da8b6 100644 --- a/core-tests/include/test_core.h +++ b/core-tests/include/test_core.h @@ -16,21 +16,49 @@ #include #include - #define TEST_HOST "127.0.0.1" #define TEST_PORT 9501 #define TEST_TMP_FILE "/tmp/swoole_core_test_file" -#define TEST_TMP_DIR "/tmp/swoole_core_test_dir" +#define TEST_TMP_DIR "/tmp/swoole_core_test_dir" #define TEST_JPG_FILE "/examples/test.jpg" -#define TEST_JPG_MD5SUM "64a42b4c0f3c65a14c23b60d3880a917" -#define TEST_HTTP_PROXY_PORT 8888 +#define TEST_JPG_MD5SUM "64a42b4c0f3c65a14c23b60d3880a917" + #define TEST_HTTP_PROXY_HOST "127.0.0.1" +#define TEST_HTTP_PROXY_PORT 8888 +#define TEST_HTTP_PROXY_USER "user" +#define TEST_HTTP_PROXY_PASSWORD "password" + +#define TEST_SOCKS5_PROXY_HOST "127.0.0.1" +#define TEST_SOCKS5_PROXY_PORT 8080 +#define TEST_SOCKS5_PROXY_USER "user" +#define TEST_SOCKS5_PROXY_PASSWORD "password" + +#define TEST_DOMAIN_BAIDU "www.baidu.com" -#define ASSERT_MEMEQ(x,y,n) ASSERT_EQ(memcmp((x), (y), n), 0) -#define EXPECT_MEMEQ(x,y,n) EXPECT_EQ(memcmp((x), (y), n), 0) +#define TEST_REQUEST_BAIDU \ + "GET / HTTP/1.1\r\n" \ + "Host: www.baidu.com\r\n" \ + "Connection: close\r\n" \ + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " \ + "Chrome/51.0.2704.106 Safari/537.36" \ + "\r\n\r\n" + +#define ASSERT_MEMEQ(x, y, n) ASSERT_EQ(memcmp((x), (y), n), 0) +#define EXPECT_MEMEQ(x, y, n) EXPECT_EQ(memcmp((x), (y), n), 0) + +namespace swoole { +struct HttpProxy; +struct Socks5Proxy; +namespace test { -namespace swoole { namespace test { const std::string &get_root_path(); std::string get_jpg_file(); bool is_github_ci(); -}}; + +int get_random_port(); + +Socks5Proxy *create_socks5_proxy(); +HttpProxy *create_http_proxy(); + +} // namespace test +}; // namespace swoole diff --git a/core-tests/include/test_coroutine.h b/core-tests/include/test_coroutine.h index baf6a4933b4..8843f5f0028 100644 --- a/core-tests/include/test_coroutine.h +++ b/core-tests/include/test_coroutine.h @@ -8,68 +8,66 @@ #include "swoole_coroutine_socket.h" #include "swoole_coroutine_c_api.h" -namespace swoole { namespace test { +namespace swoole { +namespace test { -class coroutine -{ -public: - coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) : - fn(_fn), arg(_arg), complete_num(_complete_num) { } +class coroutine { + public: + coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) + : fn(_fn), arg(_arg), complete_num(_complete_num) {} - void start() - { + void start() { fn(arg); (*complete_num)++; } - inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) - { + inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) { auto test = new coroutine(fn, arg, complete_num); - long cid = swoole::Coroutine::create([](void *arg) - { - ((coroutine *) arg)->start(); - delete (coroutine *) arg; - }, test); + long cid = swoole::Coroutine::create( + [](void *arg) { + ((coroutine *) arg)->start(); + delete (coroutine *) arg; + }, + test); ASSERT_GT(cid, 0); } - inline static void run(std::initializer_list> args) - { + inline static void run(std::initializer_list> args) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - for (const auto &arg : args) - { + Coroutine::activate(); + for (const auto &arg : args) { create(arg.first, arg.second, &complete_num); } swoole_event_wait(); + Coroutine::deactivate(); } - inline static void run(std::initializer_list fns) - { + inline static void run(std::initializer_list fns) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - for (const auto &fn : fns) - { + Coroutine::activate(); + for (const auto &fn : fns) { create(fn, nullptr, &complete_num); } swoole_event_wait(); + Coroutine::deactivate(); } - inline static void run(const CoroutineFunc &fn, void *arg = nullptr) - { + inline static void run(const CoroutineFunc &fn, void *arg = nullptr) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); create(fn, arg, &complete_num); swoole_event_wait(); + Coroutine::deactivate(); } -private: + private: CoroutineFunc fn; void *arg; int *complete_num; }; -} -} - - +} // namespace test +} // namespace swoole diff --git a/core-tests/include/test_server.h b/core-tests/include/test_server.h index bd1234f250a..6199b4cf8ff 100644 --- a/core-tests/include/test_server.h +++ b/core-tests/include/test_server.h @@ -5,15 +5,15 @@ #define SERVER_THIS ((swoole::test::Server *) serv->private_data_2) -#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id -#define ON_PACKET_PARAMS swServer *serv, swRecvData *req -#define ON_RECEIVE_PARAMS swServer *serv, swRecvData *req +#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id +#define ON_PACKET_PARAMS swServer *serv, swRecvData *req +#define ON_RECEIVE_PARAMS swServer *serv, swRecvData *req typedef void (*_onStart)(swServer *serv); typedef void (*_onShutdown)(swServer *serv); typedef void (*_onPipeMessage)(swServer *, swEventData *data); -typedef void (*_onWorkerStart)(swServer *serv, int worker_id); -typedef void (*_onWorkerStop)(swServer *serv, int worker_id); +typedef void (*_onWorkerStart)(swServer *serv, swoole::Worker *worker); +typedef void (*_onWorkerStop)(swServer *serv, swoole::Worker *worker); typedef int (*_onReceive)(swServer *, swRecvData *); typedef int (*_onPacket)(swServer *, swRecvData *); typedef void (*_onClose)(swServer *serv, swDataHead *); @@ -23,11 +23,11 @@ using on_workerstart_lambda_type = void (*)(ON_WORKERSTART_PARAMS); using on_receive_lambda_type = void (*)(ON_RECEIVE_PARAMS); using on_packet_lambda_type = void (*)(ON_PACKET_PARAMS); -namespace swoole { namespace test { +namespace swoole { +namespace test { //-------------------------------------------------------------------------------------------------------- -class Server -{ -private: +class Server { + private: swoole::Server serv; std::vector ports; std::unordered_map private_data; @@ -36,7 +36,7 @@ class Server int mode; int type; -public: + public: DgramPacket *packet = nullptr; Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type); @@ -48,23 +48,19 @@ class Server ssize_t sendto(const swoole::network::Address &address, const char *__buf, size_t __n, int server_socket = -1); int close(int session_id, int reset); - inline void* get_private_data(const std::string &key) - { + inline void *get_private_data(const std::string &key) { auto it = private_data.find(key); - if (it == private_data.end()) - { + if (it == private_data.end()) { return nullptr; - } - else - { + } else { return it->second; } } - inline void set_private_data(const std::string &key, void *data) - { + inline void set_private_data(const std::string &key, void *data) { private_data[key] = data; } }; //-------------------------------------------------------------------------------------------------------- -}} +} // namespace test +} // namespace swoole diff --git a/core-tests/run.sh b/core-tests/run.sh index 7c9dd0eb82b..eac533dc4d8 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,5 +1,32 @@ #!/bin/bash -cmake . +__DIR__=$(cd "$(dirname "$0")";pwd) +__SWOOLE_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) + +if [ "${SWOOLE_ENABLE_ASAN}" = 1 ]; then + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 -D enable_asan=1 +else + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 +fi make -j8 ipcs -q -./bin/core_tests + +tasks=$(./bin/core_tests --gtest_list_tests | awk '/\./') +for task in $tasks; do + + if [ "${SWOOLE_VALGRIND}" = 1 ]; then + # --leak-check=full --show-leak-kinds=all --track-origins=yes + execute_command="valgrind ./bin/core_tests" + else + execute_command="./bin/core_tests" + fi + + if [ $task = "log." ]; then + $execute_command --gtest_filter=$task* + else + sudo -E $execute_command --gtest_filter=$task* + fi + + if [ $? -ne 0 ] && [ "${GITHUB_ACTIONS}" = true ]; then + exit 255 + fi +done diff --git a/core-tests/samples/CMakeLists.txt b/core-tests/samples/CMakeLists.txt index 97074e8d933..408e7ab9318 100755 --- a/core-tests/samples/CMakeLists.txt +++ b/core-tests/samples/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 2.8) project(samples) #set(CMAKE_BUILD_TYPE Released) set(CMAKE_CXX_STANDARD 11) -file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS *.cc) +file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cc) add_definitions(-DHAVE_CONFIG_H) link_directories($ENV{SWOOLE_DIR}/lib) -include_directories(./include ./ $ENV{SWOOLE_DIR}/ $ENV{SWOOLE_DIR}/include/ BEFORE) +include_directories(. ./include $ENV{SWOOLE_DIR} $ENV{SWOOLE_DIR}/include BEFORE) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(core_samples ${SOURCE_FILES}) target_link_libraries(core_samples swoole) diff --git a/core-tests/samples/README.md b/core-tests/samples/README.md deleted file mode 100644 index 5af1d542c70..00000000000 --- a/core-tests/samples/README.md +++ /dev/null @@ -1,20 +0,0 @@ -编译 swoole.so ------- -`git clone`源代码,然后设置环境变量`export SWOOLE_DIR=/home/htf/workspace/swoole-src` - -```shell -cd swoole-src -phpize -./configure -cmake . -make -j -``` - -编译示例程序 ------ -```shell -cd swoole-src/core-tests/samples -cmake . -make -j -./bin/core_samples -``` diff --git a/core-tests/samples/s1.cc b/core-tests/samples/s1.cc deleted file mode 100644 index a9fa1dc7dbe..00000000000 --- a/core-tests/samples/s1.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include "swoole.h" -#include "swoole_api.h" -#include "swoole_client.h" -#include "swoole_server.h" -#include "swoole_coroutine.h" -#include "swoole_coroutine_socket.h" -#include "swoole_coroutine_system.h" - -#include - -using swoole::Coroutine; -using swoole::coroutine::System; -using swoole::coroutine::Socket; -using namespace std; - -struct A -{ - int x; - int *y; -}; - -static A G_a = -{ 0, 0 }; - -int main(int argc, char **argv) -{ - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - /** - * 协程1 - */ - Coroutine::create([](void *arg) - { - - G_a.x = 1234; - int y = 5678; - G_a.y = &y; - /** - * 这里协程挂起后,协程2 会执行,在协程2中修改了 x, y 值 - * 协程2 退出或挂起后,重新回到协程1,这里的x和y的值已经不符合预期了 - */ - System::sleep(1); - //这里会显示 100 - cout << "X=" << G_a.x << endl; - //这里会读到空指针 - cout << "Y=" << *G_a.y << endl; - }); - - /** - * 协程2 - */ - Coroutine::create([](void *arg) - { - G_a.x = 100; - G_a.y = nullptr; - }); - - swoole_event_wait(); - - return 0; -} diff --git a/core-tests/samples/src/s1.cc b/core-tests/samples/src/s1.cc new file mode 100644 index 00000000000..e8865cc48c6 --- /dev/null +++ b/core-tests/samples/src/s1.cc @@ -0,0 +1,49 @@ +#include "swoole.h" +#include "swoole_api.h" +#include "swoole_client.h" +#include "swoole_server.h" +#include "swoole_coroutine.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" + +#include + +using swoole::Coroutine; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using namespace std; + +struct A { + int x; + int *y; +}; + +static A G_a = {0, 0}; + +int main(int argc, char **argv) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + // coroutine 1 + Coroutine::create([](void *arg) { + G_a.x = 1234; + int y = 5678; + G_a.y = &y; + // After the coroutine 1 is suspended here, the coroutine 2 will be executed, and the x, y values is updated in + // the coroutine 2. After the coroutine 2 suspends, go back to coroutine 1, where the values of x and y will be + // no longer as expected + System::sleep(1); + // output 100 + cout << "X=" << G_a.x << endl; + // read invalid point + cout << "Y=" << *G_a.y << endl; + }); + + // coroutine 2 + Coroutine::create([](void *arg) { + G_a.x = 100; + G_a.y = nullptr; + }); + + swoole_event_wait(); + + return 0; +} diff --git a/core-tests/src/_lib/http.cpp b/core-tests/src/_lib/http.cpp index bc78ccef284..3f6f3fe1395 100644 --- a/core-tests/src/_lib/http.cpp +++ b/core-tests/src/_lib/http.cpp @@ -52,15 +52,20 @@ std::shared_ptr Client::Recv() { if (strm.read(buf, SW_WEBSOCKET_HEADER_LEN) <= 0) { return false; } - packet_len = proto.get_package_length(&proto, nullptr, buf, 2); + swoole::PacketLength pl { + buf, + SW_WEBSOCKET_HEADER_LEN, + }; + packet_len = proto.get_package_length(&proto, nullptr, &pl); if (packet_len < 0) { return false; } if (packet_len == 0) { - if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, proto.real_header_length - SW_WEBSOCKET_HEADER_LEN) <= 0) { + if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, pl.header_len - SW_WEBSOCKET_HEADER_LEN) <= 0) { return false; } - packet_len = proto.get_package_length(&proto, nullptr, buf, proto.real_header_length); + pl.buf_size = pl.header_len; + packet_len = proto.get_package_length(&proto, nullptr, &pl); if (packet_len <= 0) { return false; } @@ -72,7 +77,7 @@ std::shared_ptr Client::Recv() { } data[packet_len] = 0; - uint32_t header_len = proto.real_header_length > 0 ? proto.real_header_length : SW_WEBSOCKET_HEADER_LEN; + uint32_t header_len = pl.header_len > 0 ? pl.header_len : SW_WEBSOCKET_HEADER_LEN; memcpy(data, buf, header_len); ssize_t read_bytes = header_len; diff --git a/core-tests/src/_lib/server.cpp b/core-tests/src/_lib/server.cpp index e38d2ea8c83..dac4afe9093 100644 --- a/core-tests/src/_lib/server.cpp +++ b/core-tests/src/_lib/server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index 47827a8d411..671a0aa7235 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -21,6 +21,8 @@ #include "swoole_server.h" #include "swoole_file.h" #include "swoole_util.h" +#include "swoole.h" +#include "swoole_config.h" using namespace swoole; using namespace std; @@ -32,6 +34,7 @@ TEST(base, datahead_dump) { data.fd = 123; char buf[128]; size_t n = data.dump(buf, sizeof(buf)); + data.print(); ASSERT_GT(std::string(buf, n).find("int fd = 123;"), 1); } @@ -73,11 +76,11 @@ TEST(base, file_get_size) { ASSERT_TRUE(f.ready()); f.truncate(0); - f.set_offest(0); + f.set_offset(0); f.write(buf, sizeof(buf) - 1); f.close(); - ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) -1); + ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) - 1); } TEST(base, version_compare) { @@ -110,6 +113,23 @@ TEST(base, shell_exec) { ASSERT_GT(n, 0); ASSERT_STREQ(string(buf).substr(0, sizeof(TEST_JPG_MD5SUM) - 1).c_str(), TEST_JPG_MD5SUM); close(_pipe); + + str = "md5sum test.abcdef"; + _pipe = swoole_shell_exec(str.c_str(), &pid, 1); + memset(buf, 0, sizeof(buf)); + ssize_t length = 0; + while (1) { + n = read(_pipe, buf + length, sizeof(buf) - 1 - length); + length += n; + if (n > 0) { + continue; + } + break; + } + ASSERT_GT(length, 0); + + ASSERT_STREQ(buf, string("md5sum: test.abcdef: No such file or directory\n").c_str()); + close(_pipe); } TEST(base, file_size) { @@ -123,12 +143,12 @@ TEST(base, file_size) { } TEST(base, eventdata_pack) { - EventData ed1 { }; + EventData ed1{}; ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length())); ASSERT_EQ(string(ed1.data, ed1.info.len), test_data); - EventData ed2 { }; + EventData ed2{}; ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG); ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); @@ -166,6 +186,14 @@ TEST(base, dirname) { } TEST(base, set_task_tmpdir) { + ASSERT_FALSE(swoole_set_task_tmpdir("aaa")); + + size_t length = SW_TASK_TMP_PATH_SIZE + 1; + char too_long_dir[length] = {}; + swoole_random_string(too_long_dir + 1, length - 1); + too_long_dir[0] = '/'; + ASSERT_FALSE(swoole_set_task_tmpdir(too_long_dir)); + const char *tmpdir = "/tmp/swoole/core_tests/base"; ASSERT_TRUE(swoole_set_task_tmpdir(tmpdir)); File fp = swoole::make_tmpfile(); @@ -188,27 +216,22 @@ TEST(base, set_task_tmpdir) { TEST(base, version) { ASSERT_STREQ(swoole_version(), SWOOLE_VERSION); ASSERT_EQ(swoole_version_id(), SWOOLE_VERSION_ID); + ASSERT_EQ(swoole_api_version_id(), SWOOLE_API_VERSION_ID); } static std::string test_func(std::string test_data_2) { return test_data + test_data_2; } -TEST(base, add_function) { - typedef std::string (*_func_t)(std::string); - swoole_add_function("test_func", (void *) test_func); - _func_t _func = (_func_t) swoole_get_function(SW_STRL("test_func")); - std::string b = ", swoole is best"; - auto rs = _func(", swoole is best"); - ASSERT_EQ(rs, test_data + b); -} - TEST(base, hook) { int count = 0; - swoole_add_hook(SW_GLOBAL_HOOK_END, [](void *data) -> void { - int *_count = (int *) data; - *_count = 9999; - }, 1); + swoole_add_hook( + SW_GLOBAL_HOOK_END, + [](void *data) -> void { + int *_count = (int *) data; + *_count = 9999; + }, + 1); ASSERT_TRUE(swoole_isset_hook(SW_GLOBAL_HOOK_END)); swoole_call_hook(SW_GLOBAL_HOOK_END, &count); ASSERT_EQ(count, 9999); @@ -217,10 +240,10 @@ TEST(base, hook) { TEST(base, intersection) { std::vector vec1{"index.php", "index.html", "default.html"}; - std::set vec2 {".", "..", "default.html", "index.php", "test.html", "a.json", "index.php"}; + std::set vec2{".", "..", "default.html", "index.php", "test.html", "a.json", "index.php"}; ASSERT_EQ("index.php", swoole::intersection(vec1, vec2)); - std::set vec3 {"a", "zh中", "、r\n"}; + std::set vec3{"a", "zh中", "、r\n"}; ASSERT_EQ("", swoole::intersection(vec1, vec3)); } @@ -232,3 +255,57 @@ TEST(base, itoa) { ASSERT_EQ(n, 9); ASSERT_STREQ(buf, "123456987"); } + +TEST(base, get_systemd_listen_fds) { + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT + 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT - 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), SW_MAX_LISTEN_PORT - 1); +} + +TEST(base, type_size) { + ASSERT_EQ(swoole_type_size('c'), 1); + ASSERT_EQ(swoole_type_size('s'), 2); + ASSERT_EQ(swoole_type_size('l'), 4); + ASSERT_EQ(swoole_type_size('b'), 0); // default value +} + +size_t swoole_fatal_error_impl(const char *format, ...) { + size_t retval = 0; + va_list args; + va_start(args, format); + + char buf[128]; + retval += sw_vsnprintf(buf, 128, format, args); + va_end(args); + return retval; +} + +TEST(base, vsnprintf) { + ASSERT_GT(swoole_fatal_error_impl("Hello %s", "World!!!"), 0); +} + +TEST(base, log_level) { + int level = sw_logger()->get_level(); + swoole_set_log_level(SW_LOG_TRACE); + swoole_print_backtrace(); + EXPECT_EQ(SW_LOG_TRACE, sw_logger()->get_level()); + swoole_set_log_level(level); +} + +TEST(base, trace_flag) { + int flags = SwooleG.trace_flags; + swoole_set_trace_flags(SW_TRACE_CARES); + EXPECT_EQ(SW_TRACE_CARES, SwooleG.trace_flags); + swoole_set_trace_flags(flags); +} + +TEST(base, only_dump) { + // just dump something + std::string data = "hello world"; + swoole_dump_ascii(data.c_str(), data.length()); + swoole_dump_bin(data.c_str(), 'C', data.length()); + swoole_dump_hex(data.c_str(), data.length()); + ASSERT_TRUE(true); +} diff --git a/core-tests/src/core/channel.cpp b/core-tests/src/core/channel.cpp index c0a8df25966..ec756f092e4 100644 --- a/core-tests/src/core/channel.cpp +++ b/core-tests/src/core/channel.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -86,3 +86,38 @@ TEST(channel, push) { c->destroy(); } + +TEST(channel, peek) { + char buf[8000]; + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + ASSERT_EQ(c->peek(buf, sizeof(buf)), SW_ERR); + + string value = "test"; + c->push(value.c_str(), value.length()); + ASSERT_EQ(c->peek((void *) buf, sizeof(buf)), value.length()); + c->destroy(); +} + +TEST(channel, notify) { + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + thread t1([&]() { + sleep(0.02); + string value = "test"; + c->push(value.c_str(), value.length()); + c->notify(); + }); + + thread t2([&]() { + while (c->wait()) { + char buf[8000]; + ASSERT_GT(c->pop((void *) buf, sizeof(buf)), 0); + break; + } + }); + + t1.join(); + t2.join(); + + c->print(); + c->destroy(); +} diff --git a/core-tests/src/core/hash.cpp b/core-tests/src/core/hash.cpp index 20e2318fa84..7d7f1cd5ebb 100644 --- a/core-tests/src/core/hash.cpp +++ b/core-tests/src/core/hash.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/core/heap.cpp b/core-tests/src/core/heap.cpp index a4ebd4df571..226c9838031 100644 --- a/core-tests/src/core/heap.cpp +++ b/core-tests/src/core/heap.cpp @@ -12,21 +12,26 @@ typedef struct node_t { TEST(heap, random) { node_t *ns; node_t *n; - swoole::Heap pq(SIZE, swoole::Heap::MAX_HEAP); - std::map _map; + ASSERT_EQ(pq.peek(), nullptr); int i; - for (i = 0; i < SIZE - 1; i++) { + for (i = 0; i < SIZE * 2 - 1; i++) { int pri = swoole_system_random(10000, 99999); ns = (node_t *) malloc(sizeof(node_t)); ns->val = i; ns->pri = pri; pq.push(pri, ns); _map[i] = pri; + + if (0 == i) { + pq.print(); // print once + } } + n = (node_t *) pq.peek(); + ASSERT_EQ(_map[n->val], n->pri); while ((n = (node_t *) pq.pop())) { ASSERT_EQ(_map[n->val], n->pri); free(n); diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp index 06c93351a44..af76a7483cb 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -1,28 +1,37 @@ #include "test_core.h" #include "swoole_file.h" #include +#include using namespace swoole; const char *file = "/tmp/swoole_log_test.log"; TEST(log, level) { - sw_logger()->reset(); - sw_logger()->set_level(SW_LOG_NOTICE); - sw_logger()->open(file); + std::vector processTypes = {SW_PROCESS_MASTER, SW_PROCESS_MANAGER, SW_PROCESS_WORKER, SW_PROCESS_TASKWORKER}; - sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); - sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); + int originType = swoole_get_process_type(); + for (auto iter = processTypes.begin(); iter != processTypes.end(); iter++) { + SwooleG.process_type = *iter; + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); - auto content = file_get_contents(file); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); - sw_logger()->close(); - unlink(file); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); - ASSERT_FALSE(content->contains(SW_STRL("hello info"))); - ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); - ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + ASSERT_FALSE(content->contains(SW_STRL("hello info"))); + ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); + ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + + SwooleG.process_type = originType; + } } TEST(log, date_format) { @@ -85,29 +94,40 @@ TEST(log, date_with_microseconds) { } TEST(log, rotation) { - sw_logger()->reset(); - sw_logger()->set_rotation(SW_LOG_ROTATION_DAILY); - sw_logger()->open(file); - - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); - - ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); - ASSERT_EQ(errno, ENOENT); - ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); - - sw_logger()->close(); - unlink(sw_logger()->get_real_file()); + std::vector types = { + SW_LOG_ROTATION_DAILY, SW_LOG_ROTATION_EVERY_MINUTE, SW_LOG_ROTATION_HOURLY, SW_LOG_ROTATION_MONTHLY}; + for (auto iter = types.begin(); iter != types.end(); iter++) { + sw_logger()->reset(); + sw_logger()->set_rotation(*iter); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_DEBUG, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_TRACE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_ERROR, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello world")); + + ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); + ASSERT_EQ(errno, ENOENT); + ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); + + sw_logger()->close(); + unlink(sw_logger()->get_real_file()); + } } TEST(log, redirect) { - char *p = getenv("GITHUB_ACTIONS"); - if (p) { + if (test::is_github_ci()) { return; } - sw_logger()->reset(); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // no log file opened + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(0)); // no redirected + ASSERT_TRUE(sw_logger()->open(file)); ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(1)); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // has been redirected printf("hello world\n"); auto content = file_get_contents(file); @@ -171,3 +191,25 @@ TEST(log, pretty_name_lambda) { TestA::test_pretty_name_lambda(false, "TestA::test_pretty_name_lambda"); test_pretty_name_lambda(false, "test_pretty_name_lambda"); } + +TEST(log, ignore_error) { + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); + + const int ignored_errcode = 999999; + const int errcode = 888888; + + swoole_ignore_error(ignored_errcode); + + swoole_error_log(SW_LOG_WARNING, ignored_errcode, "error 1"); + swoole_error_log(SW_LOG_WARNING, errcode, "error 2"); + + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + ASSERT_FALSE(content->contains(SW_STRL("error 1"))); + ASSERT_TRUE(content->contains(SW_STRL("error 2"))); +} diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index 1f4fee8c352..6e285616e34 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -2,6 +2,32 @@ #include "swoole_util.h" using namespace std; +using swoole::String; + +TEST(string, ltrim) { + char buf[1024]; + char *ptr_buf; + strcpy(buf, " hello world"); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("hello world", ptr_buf), 0); + ASSERT_NE(strcmp(" hello world", ptr_buf), 0); + + strcpy(buf, " "); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strlen(ptr_buf), 0); + + memcpy(buf, " a\0b\0", 6); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("a", ptr_buf), 0); + + buf[0] = '\0'; + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("", ptr_buf), 0); +} TEST(string, rtrim) { char buf[1024]; @@ -13,6 +39,10 @@ TEST(string, rtrim) { strcpy(buf, " "); swoole::rtrim(buf, strlen(buf)); ASSERT_EQ(strlen(buf), 0); + + buf[0] = '\0'; + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strcmp("", buf), 0); } TEST(string, strnpos) { @@ -38,17 +68,19 @@ TEST(string, strnstr) { { string haystack = "hello world"; string needle = " "; - const char *pos; - - pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); ASSERT_EQ(haystack.c_str() + 5, pos); } { string haystack = "hello world"; string needle = "*"; - const char *pos; - - pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(NULL, pos); + } + { + string haystack = "hello world\r\n"; + string needle = "\r\n\r\n"; + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); ASSERT_EQ(NULL, pos); } } @@ -57,7 +89,7 @@ TEST(string, explode) { string haystack = "hello world"; string needle = " "; - swString str; + String str; str.str = (char *) haystack.c_str(); str.length = haystack.length(); @@ -82,7 +114,7 @@ TEST(string, explode_2) { string haystack = "hello,world,swoole,php,last"; string needle = ","; - swString str; + String str; str.str = (char *) haystack.c_str(); str.length = haystack.length(); @@ -109,7 +141,7 @@ static string test_data = "hello,world,swoole,php,last"; TEST(string, pop_1) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); char *str_1 = str->str; @@ -128,7 +160,7 @@ TEST(string, pop_1) { TEST(string, pop_2) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); char *str_1 = str->str; @@ -147,7 +179,7 @@ TEST(string, pop_2) { TEST(string, reduce_1) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); const int len_1 = 11; str->append(test_data.c_str(), test_data.length()); @@ -160,7 +192,7 @@ TEST(string, reduce_1) { TEST(string, reduce_2) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); str->append(test_data.c_str(), test_data.length()); str->offset = str->length; @@ -172,7 +204,7 @@ TEST(string, reduce_2) { TEST(string, reduce_3) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); str->append(test_data.c_str(), test_data.length()); str->offset = 0; @@ -182,18 +214,30 @@ TEST(string, reduce_3) { EXPECT_EQ(str->length, test_data.length()); } -TEST(string, format) { - swString str(128); +const auto FORMAT_INT = 999999999999999; +const auto FORMAT_STR = "hello world"; - int a = swoole_rand(1000000, 9000000); +TEST(string, format_1) { + String str1(1024); + str1.append_random_bytes(1024, true); - swString str2(1024); - str2.append_random_bytes(1024, true); + size_t n = str1.format("str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + std::string str2("str=hello world, value=999999999999999"); - str.format("a=%d, b=%.*s\r\n", a, str2.length, str2.str); + ASSERT_EQ(str1.get_length(), n); + ASSERT_MEMEQ(str1.value(), str2.c_str(), n); +} + +TEST(string, format_2) { + String str1(1024); + str1.append_random_bytes(1024, true); + + std::string str2(str1.value(), str1.get_length()); - EXPECT_GT(str.size, 1024); - EXPECT_STREQ(str.str + str.length - 2, "\r\n"); + size_t n = str1.format_impl(String::FORMAT_APPEND, "str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + str2 += std::string(str1.value() + str2.length(), n); + + EXPECT_MEMEQ(str1.value(), str2.c_str(), str1.get_length()); } TEST(string, substr_len) { @@ -201,3 +245,29 @@ TEST(string, substr_len) { ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', true), 5); ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', false), 15); } + +TEST(string, starts_with) { + const char *str1 = "hello world"; + ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), SW_STRL("hello"))); + ASSERT_FALSE(swoole::starts_with(str1, strlen(str1), SW_STRL("php"))); + ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), str1, strlen(str1))); +} + +TEST(string, ends_with) { + const char *str1 = "hello world"; + ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), SW_STRL("world"))); + ASSERT_FALSE(swoole::ends_with(str1, strlen(str1), SW_STRL("php"))); + ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), str1, strlen(str1))); +} + +TEST(string, append_number) { + string data = "hello"; + auto str = swoole::make_string(data.length() + 32); + str->append(data.c_str(), data.length()); + str->append(123); + str->set_null_terminated(); + EXPECT_STREQ(str->str, data.append("123").c_str()); + + str->print(true); + str->print(false); +} diff --git a/core-tests/src/core/time.cpp b/core-tests/src/core/time.cpp index bfeb44607a3..80107f62d24 100644 --- a/core-tests/src/core/time.cpp +++ b/core-tests/src/core/time.cpp @@ -22,3 +22,7 @@ TEST(time, get_seconds) { time_t sec2 = time(NULL); ASSERT_TRUE(sec1 == sec2 or sec1 == sec2 - 1); } + +TEST(time, get_timezone) { + ASSERT_GE(swoole::get_timezone(), 0); +} diff --git a/core-tests/src/core/util.cpp b/core-tests/src/core/util.cpp new file mode 100644 index 00000000000..d756d542321 --- /dev/null +++ b/core-tests/src/core/util.cpp @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_util.h" + +TEST(util, bitmap) { + swoole::BitMap m(4096); + + m.set(199); + m.set(1234); + m.set(3048); + + ASSERT_EQ(m.get(199), true); + ASSERT_EQ(m.get(1234), true); + ASSERT_EQ(m.get(3048), true); + + ASSERT_EQ(m.get(2048), false); + ASSERT_EQ(m.get(128), false); + + m.unset(1234); + ASSERT_EQ(m.get(1234), false); + + m.clear(); +} diff --git a/core-tests/src/coroutine/async.cpp b/core-tests/src/coroutine/async.cpp index 8e46f3a1edb..4de8af590bf 100644 --- a/core-tests/src/coroutine/async.cpp +++ b/core-tests/src/coroutine/async.cpp @@ -1,9 +1,6 @@ #include "test_coroutine.h" -#ifdef HAVE_SWOOLE_DIR #include "swoole_async.h" -#else -#include "swoole/swoole_async.h" -#endif + #include #include @@ -57,7 +54,7 @@ TEST(coroutine_async, error) { coroutine::run([](void *arg) { int retval = 0x7009501; const char *test_file = "/tmp/swoole_core_test_file_not_exists"; - swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); }, -1); + swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); }); ASSERT_EQ(retval, -1); ASSERT_EQ(errno, ENOENT); }); diff --git a/core-tests/src/coroutine/base.cpp b/core-tests/src/coroutine/base.cpp index 0a30d3ead15..442bb477d6f 100644 --- a/core-tests/src/coroutine/base.cpp +++ b/core-tests/src/coroutine/base.cpp @@ -34,11 +34,27 @@ TEST(coroutine_base, get_init_msec) { } TEST(coroutine_base, yield_resume) { + Coroutine::set_on_yield([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + + Coroutine::set_on_resume([](void *arg) { + Coroutine *current = Coroutine::get_current(); + ASSERT_EQ(current, nullptr); + }); + + Coroutine::set_on_close([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + long _cid; long cid = Coroutine::create( [](void *arg) { long cid = Coroutine::get_current_cid(); Coroutine *co = Coroutine::get_by_cid(cid); + co->set_task((void *) &cid); co->yield(); *(long *) arg = Coroutine::get_current_cid(); }, @@ -46,6 +62,7 @@ TEST(coroutine_base, yield_resume) { ASSERT_GT(cid, 0); Coroutine::get_by_cid(cid)->resume(); + Coroutine::set_on_close(nullptr); ASSERT_EQ(cid, _cid); } @@ -119,6 +136,7 @@ TEST(coroutine_base, get_current_cid) { auto co = Coroutine::get_current(); auto actual = co->get_cid(); ASSERT_EQ(actual, Coroutine::get_current_cid()); + ASSERT_EQ(actual, swoole_coroutine_get_current_id()); }); } @@ -174,7 +192,7 @@ TEST(coroutine_base, get_elapsed) { } TEST(coroutine_base, run) { - long cid = coroutine::run([](void *ptr){ + long cid = coroutine::run([](void *ptr) { }); ASSERT_GE(cid, 1); @@ -183,7 +201,7 @@ TEST(coroutine_base, run) { TEST(coroutine_base, cancel) { coroutine::run([](void *arg) { auto co = Coroutine::get_current_safe(); - Coroutine::create([co](void *){ + Coroutine::create([co](void *) { System::sleep(0.002); co->cancel(); }); @@ -199,3 +217,20 @@ TEST(coroutine_base, timeout) { ASSERT_EQ(co->is_timedout(), true); }); } + +TEST(coroutine_base, gdb) { + Coroutine::create([](void *) { + Coroutine *current = Coroutine::get_current(); + long cid = current->get_cid(); + ASSERT_EQ(swoole_coroutine_count(), 1); + ASSERT_EQ(swoole_coroutine_get(cid), current); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + ASSERT_EQ(swoole_coroutine_iterator_each(), nullptr); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + Coroutine::print_list(); + }); +} diff --git a/core-tests/src/coroutine/channel.cpp b/core-tests/src/coroutine/channel.cpp index 3074a027eed..cb24673e5c0 100644 --- a/core-tests/src/coroutine/channel.cpp +++ b/core-tests/src/coroutine/channel.cpp @@ -89,3 +89,39 @@ TEST(coroutine_channel, pop_timeout) { ASSERT_EQ(ret, nullptr); }); } + +TEST(coroutine_channel, close) { + Channel chan(1); + coroutine::run( + [](void *arg) { + int value = 1; + auto chan = (Channel *) arg; + while (1) { + if (!chan->push((void *) &value)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_FALSE(chan->push(nullptr)); + break; + } + } + }, + &chan); + + ASSERT_TRUE(chan.close()); + ASSERT_FALSE(chan.close()); + + Channel chan2(1); + coroutine::run( + [](void *arg) { + auto chan = (Channel *) arg; + while (1) { + if (!chan->pop(0)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_EQ(chan->pop(), nullptr); + break; + } + } + }, + &chan2); + + ASSERT_TRUE(chan2.close()); +} diff --git a/core-tests/src/coroutine/gethostbyname.cpp b/core-tests/src/coroutine/gethostbyname.cpp index 02a009c4103..042999d61af 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -5,14 +5,17 @@ using swoole::Timer; using swoole::coroutine::System; using swoole::test::coroutine; +const char *domain_baidu = "www.baidu.com"; +const char *domain_tencent = "www.tencent.com"; + TEST(coroutine_gethostbyname, resolve_cache) { coroutine::run([](void *arg) { System::set_dns_cache_capacity(10); - std::string addr1 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr1 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr1, ""); int64_t with_cache = Timer::get_absolute_msec(); for (int i = 0; i < 100; ++i) { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_EQ(addr1, addr2); } with_cache = Timer::get_absolute_msec() - with_cache; @@ -20,7 +23,7 @@ TEST(coroutine_gethostbyname, resolve_cache) { System::set_dns_cache_capacity(0); int64_t without_cache = Timer::get_absolute_msec(); for (int i = 0; i < 5; ++i) { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr2, ""); } without_cache = Timer::get_absolute_msec() - without_cache; @@ -31,7 +34,7 @@ TEST(coroutine_gethostbyname, resolve_cache) { TEST(coroutine_gethostbyname, impl_async) { coroutine::run([](void *arg) { - auto result = swoole::coroutine::gethostbyname_impl_with_async("www.baidu.com", AF_INET); + auto result = swoole::coroutine::gethostbyname_impl_with_async(domain_baidu, AF_INET); ASSERT_EQ(result.empty(), false); }); } @@ -61,3 +64,23 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) { ASSERT_LT(Timer::get_absolute_msec() - start, 5); }); } + +TEST(coroutine_gethostbyname, dns_expire) { + coroutine::run([](void *arg) { + time_t expire = 0.2; + System::set_dns_cache_expire(expire); + System::gethostbyname(domain_tencent, AF_INET); + + int64_t with_cache = Timer::get_absolute_msec(); + System::gethostbyname(domain_tencent, AF_INET); + with_cache = Timer::get_absolute_msec() - with_cache; + + sleep(0.3); + int64_t without_cache = Timer::get_absolute_msec(); + System::gethostbyname(domain_tencent, AF_INET); + without_cache = Timer::get_absolute_msec() - without_cache; + + ASSERT_GE(without_cache, with_cache); + System::clear_dns_cache(); + }); +} diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp index cb55525a4a5..1aee8368af9 100644 --- a/core-tests/src/coroutine/hook.cpp +++ b/core-tests/src/coroutine/hook.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -21,8 +21,11 @@ #include "swoole_file.h" #include "swoole_util.h" +using namespace swoole::test; + using swoole::Coroutine; using swoole::String; +using swoole::coroutine::Socket; using swoole::coroutine::System; using swoole::test::coroutine; @@ -235,10 +238,8 @@ TEST(coroutine_hook, read_dir) { swoole_coroutine_closedir(fp); }; - - coroutine::run([&](void *arg) { - fn(); - }); + + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -254,9 +255,7 @@ TEST(coroutine_hook, readlink) { ASSERT_STREQ(buf1, buf2); }; - coroutine::run([&](void *arg) { - fn(); - }); + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -278,9 +277,7 @@ TEST(coroutine_hook, stdio_1) { unlink(test_file); }; - coroutine::run([&](void *arg) { - fn(); - }); + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -311,8 +308,304 @@ TEST(coroutine_hook, stdio_2) { unlink(test_file); }; + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, sleep) { coroutine::run([&](void *arg) { - fn(); + const int sec = 1; + long sec_1 = swoole::time(); + swoole_coroutine_sleep(sec); + long sec_2 = swoole::time(); + ASSERT_LE(sec_2 - sec_1, sec); + + const int us = 2000; + long us_1 = swoole::time(); + swoole_coroutine_usleep(us); + long us_2 = swoole::time(); + ASSERT_LE(us_2 - us_1, us / 1000); + }); +} + +TEST(coroutine_hook, exists) { + coroutine::run([&](void *arg) { + const int fd = 100; // fake fd + ASSERT_EQ(swoole_coroutine_socket_create(fd), 0); + ASSERT_TRUE(swoole_coroutine_socket_exists(fd)); + auto sock = swoole_coroutine_get_socket_object(fd); + ASSERT_EQ(sock->get_fd(), fd); + swoole_coroutine_close(fd); + }); +} + +TEST(coroutine_hook, timeout) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + // unregister fd + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05), -1); + + swoole::Coroutine::create([&](void *) { + ASSERT_EQ(swoole_coroutine_socket_create(pairs[0]), 0); + + // unknown which + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], 100, 0.05), -1); + + swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05); + size_t result = swoole_coroutine_write(pairs[0], text.c_str(), length); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(result, length); + }); + + char data[length + 1]; + ASSERT_EQ(swoole_coroutine_socket_create(pairs[1]), 0); + swoole_coroutine_socket_set_timeout(pairs[1], SO_RCVTIMEO, 0.05); + size_t result = swoole_coroutine_read(pairs[1], data, length); + data[result] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_EQ(result, length); + ASSERT_STREQ(data, text.c_str()); }); - fn(); } + +TEST(coroutine_hook, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_sendmsg(pairs[0], &msg, 0); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(ret, length); + }); + + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_recvmsg(pairs[1], &msg, 0); + buf[ret] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_STREQ(buf, text.c_str()); + }); +} + +TEST(coroutine_hook, lseek) { + std::string file = get_jpg_file(); + int fd = swoole_coroutine_open(file.c_str(), O_RDONLY, 'r'); + off_t offset = swoole_coroutine_lseek(fd, 0, SEEK_SET); + swoole_coroutine_close(fd); + ASSERT_EQ(offset, 0); +} + +extern std::pair, std::shared_ptr> create_socket_pair(); + +TEST(coroutine_hook, socket_close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + auto _fd = _sock->move_fd(); + swoole_coroutine_socket_create(_fd); + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = swoole_coroutine_write(_fd, buffer->value(), buffer->get_length()); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = swoole_coroutine_read(_fd, buf, sizeof(buf)); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), 0); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + }); +} + +TEST(coroutine_hook, poll) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 1); + ASSERT_TRUE(fds[1].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + fds[1].fd = _fd1; + fds[1].events = POLLIN | POLLOUT; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + ASSERT_FALSE(fds[0].revents & POLLOUT); // not writable + ASSERT_TRUE(fds[1].revents & POLLOUT); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + }); +} + +TEST(coroutine_hook, poll_fake) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd1; + fds[0].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + bzero(fds, sizeof(pollfd)); + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 2, 1000), -1); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[0].revents & POLLOUT); + }); +} \ No newline at end of file diff --git a/core-tests/src/coroutine/http_server.cpp b/core-tests/src/coroutine/http_server.cpp index c77e71da986..373d2d8afb1 100644 --- a/core-tests/src/coroutine/http_server.cpp +++ b/core-tests/src/coroutine/http_server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -27,11 +27,12 @@ using namespace std; TEST(coroutine_http_server, get) { Server svr; mutex lock; + int port = swoole::test::get_random_port(); lock.lock(); - thread t1([&lock]() { + thread t1([&lock, port]() { lock.lock(); - Client cli(TEST_HOST, 8080); + Client cli(TEST_HOST, port); auto resp1 = cli.Get("/hi"); EXPECT_EQ(resp1->status, 200); EXPECT_EQ(resp1->body, string("Hello World!")); @@ -41,7 +42,7 @@ TEST(coroutine_http_server, get) { EXPECT_EQ(resp2->body, string("Stop Server!")); }); - coroutine::run([&lock, &svr](void *arg) { + coroutine::run([&lock, &svr, port](void *arg) { svr.Get("/hi", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); svr.Get("/stop", [&svr](const Request &req, Response &res) { @@ -53,7 +54,7 @@ TEST(coroutine_http_server, get) { svr.BeforeListen([&lock]() { lock.unlock(); }); - ASSERT_TRUE(svr.listen(TEST_HOST, 8080)); + ASSERT_TRUE(svr.listen(TEST_HOST, port)); }); t1.join(); @@ -62,12 +63,13 @@ TEST(coroutine_http_server, get) { TEST(coroutine_http_server, post) { Server svr; mutex lock; + int port = swoole::test::get_random_port(); lock.lock(); - std::thread t1([&lock]() { + std::thread t1([&lock, port]() { lock.lock(); - Client cli(TEST_HOST, 8080); + Client cli(TEST_HOST, port); httplib::Params params; params.emplace("name", "john"); @@ -82,7 +84,7 @@ TEST(coroutine_http_server, post) { EXPECT_EQ(resp2->body, string("Stop Server!")); }); - coroutine::run([&lock, &svr](void *arg) { + coroutine::run([&lock, &svr, port](void *arg) { svr.Get("/stop", [&svr](const Request &req, Response &res) { res.set_content("Stop Server!", "text/plain"); svr.stop(); @@ -92,7 +94,7 @@ TEST(coroutine_http_server, post) { svr.BeforeListen([&lock]() { lock.unlock(); }); - svr.listen(TEST_HOST, 8080); + svr.listen(TEST_HOST, port); }); t1.join(); diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 7f9423d695c..e4787c7cd5b 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -23,12 +23,21 @@ using namespace swoole::test; +using swoole::Coroutine; +using swoole::HttpProxy; using swoole::Protocol; +using swoole::Socks5Proxy; using swoole::String; using swoole::coroutine::Socket; using swoole::coroutine::System; +using swoole::network::Address; +using swoole::network::IOVector; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; using swoole::test::Server; +const std::string host = "www.baidu.com"; + TEST(coroutine_socket, connect_refused) { coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); @@ -51,21 +60,40 @@ TEST(coroutine_socket, connect_timeout) { TEST(coroutine_socket, connect_with_dns) { coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("www.baidu.com", 80); + bool retval = sock.connect(host, 80); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + }); +} + +TEST(coroutine_socket, tcp6) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP6); + bool retval = sock.connect("::1", 80); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); }); } +TEST(coroutine_socket, unixsock_fail) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_UNIX_STREAM); + bool retval = sock.connect("/tmp/unix.sock"); + ASSERT_EQ(retval, false); + ASSERT_EQ(sock.errCode, ENOENT); + }); +} + TEST(coroutine_socket, recv_success) { pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -74,9 +102,9 @@ TEST(coroutine_socket, recv_success) { sleep(1); // wait for the test server to start - coroutine::run([](void *arg) { + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect(TEST_HOST, TEST_PORT, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); sock.send(SW_STRS("hello world\n")); @@ -93,11 +121,12 @@ TEST(coroutine_socket, recv_success) { TEST(coroutine_socket, recv_fail) { pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->close(req->info.fd, 0); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -106,9 +135,9 @@ TEST(coroutine_socket, recv_fail) { sleep(1); // wait for the test server to start - coroutine::run([](void *arg) { + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect(TEST_HOST, TEST_PORT, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); sock.send("close", 6); @@ -127,6 +156,10 @@ TEST(coroutine_socket, bind_success) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("127.0.0.1", 9909); ASSERT_EQ(retval, true); + + Socket sock_1(SW_SOCK_UNIX_DGRAM); + retval = sock_1.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); }); } @@ -136,6 +169,10 @@ TEST(coroutine_socket, bind_fail) { bool retval = sock.bind("192.111.11.1", 9909); ASSERT_EQ(retval, false); ASSERT_EQ(sock.errCode, EADDRNOTAVAIL); + + Socket sock_1(SW_SOCK_TCP); + retval = sock_1.bind("127.0.0.1", 70000); + ASSERT_EQ(retval, false); }); } @@ -633,83 +670,562 @@ TEST(coroutine_socket, length_7) { TEST(coroutine_socket, event_hup) { coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + System::sleep(0.05); + char buf[1024]; + auto ret_n = conn->recv(buf, sizeof(buf)); + ASSERT_EQ(ret_n, 0); + delete conn; + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + auto buf = sock.get_read_buffer(); + Coroutine::create([&sock](void *args) { + System::sleep(0.01); + sock.shutdown(SHUT_RDWR); + }); + auto n = sock.recv_all(buf->str, buf->size); + ASSERT_EQ(sock.get_socket()->event_hup, 1); + ASSERT_EQ(n, 0); + }}); +} + +TEST(coroutine_socket, recv_line) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + conn->send("hello world\n"); + conn->send("\r"); + char buf[256]; + memset(buf, 'A', 128); + memset(buf + 128, 'B', 125); + conn->send(buf, 253); + delete conn; + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + size_t n; + auto buf = sock.get_read_buffer(); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 12); + ASSERT_MEMEQ(buf->str, "hello world\n", 12); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 1); + ASSERT_MEMEQ(buf->str, "\r", 1); + + char buf_2[256]; + memset(buf_2, 'A', 128); + memset(buf_2 + 128, 'B', 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 128); + ASSERT_MEMEQ(buf->str, buf_2, 128); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 125); + ASSERT_MEMEQ(buf->str, buf_2 + 128, 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 0); + }}); +} + +TEST(coroutine_socket, getsockname) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", 9502); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); - ASSERT_EQ(sock.listen(128), true); - Socket *conn = sock.accept(); - System::sleep(0.05); - char buf[1024]; - auto ret_n = conn->recv(buf, sizeof(buf)); - ASSERT_EQ(ret_n, 0); - delete conn; - }, + Address sa; + bool result = sock.getsockname(&sa); + sock.close(); + ASSERT_EQ(result, true); + }); +} - [](void *arg) { +TEST(coroutine_socket, check_liveness) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9502, -1); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); - ASSERT_EQ(sock.errCode, 0); - auto buf = sock.get_read_buffer(); - swoole::Coroutine::create([&sock](void *args) { - System::sleep(0.01); - sock.shutdown(SHUT_RDWR); + bool result = sock.check_liveness(); + sock.close(); + ASSERT_EQ(result, true); + result = sock.check_liveness(); + ASSERT_EQ(result, false); + }); +} + +TEST(coroutine_socket, write_and_read) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); }); - auto n = sock.recv_all(buf->str, buf->size); - ASSERT_EQ(sock.get_socket()->event_hup, 1); - ASSERT_EQ(n, 0); - }}); + + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); } -TEST(coroutine_socket, recv_line) { - coroutine::run({[](void *arg) { +TEST(coroutine_socket, write_and_read_2) { + // test for Socket::Socket(int _fd, int _domain, int _type, int _protocol) construct function + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); + + char data[128]; + Socket sock(pairs[1], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); +} + +TEST(coroutine_socket, writev_and_readv) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); + + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); + } + }); +} + +TEST(coroutine_socket, writevall_and_readvall) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); + + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); + } + }); +} + +TEST(coroutine_socket, sendfile) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + Coroutine::create([&](void *) { + std::string file = get_jpg_file(); + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + bool result = sock.sendfile(file.c_str(), 0, 0); + sock.close(); + ASSERT_TRUE(result); + }); + + char data[250000]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 250000); + data[result] = '\0'; + sock.close(); + ASSERT_GT(result, 0); + }); +} + +void test_sendto_recvfrom(enum swSocketType sock_type) { + coroutine::run([&](void *arg) { + std::string server_text = "hello world!!!"; + size_t server_length = server_text.length(); + std::string client_text = "hello swoole!!!"; + size_t client_length = client_text.length(); + + const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; + + int port = swoole::test::get_random_port(); + + Socket sock_server(sock_type); + Socket sock_client(sock_type); + sock_server.bind(ip, port); + sock_client.bind(ip, port + 1); + + ON_SCOPE_EXIT { + sock_server.close(); + sock_client.close(); + }; + + sock_server.sendto(ip, port + 1, (const void *) server_text.c_str(), server_length); + + char data_from_server[128] = {}; + struct sockaddr_in serveraddr; + bzero(&serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = inet_addr(ip); + serveraddr.sin_port = htons(port); + socklen_t addr_length = sizeof(serveraddr); + + // receive data from server + ssize_t result = + sock_client.recvfrom(data_from_server, server_length, (struct sockaddr *) &serveraddr, &addr_length); + data_from_server[result] = '\0'; + ASSERT_EQ(result, server_length); + ASSERT_STREQ(data_from_server, server_text.c_str()); + + // receive data from client + char data_from_client[128] = {}; + sock_client.sendto(ip, port, (const void *) client_text.c_str(), client_length); + result = sock_server.recvfrom(data_from_client, client_length); + data_from_client[client_length] = '\0'; + ASSERT_EQ(result, client_length); + ASSERT_STREQ(data_from_client, client_text.c_str()); + }); +} + +TEST(coroutine_socket, sendto_recvfrom_udp) { + test_sendto_recvfrom(SW_SOCK_UDP); + test_sendto_recvfrom(SW_SOCK_UDP6); +} + +void proxy_test(Socket &sock, bool https) { + if (https) { + sock.enable_ssl_encrypt(); + } + + bool retval = sock.connect(host, https ? 443 : 80); + ON_SCOPE_EXIT { + sock.close(); + }; + ASSERT_EQ(retval, true); + + if (https) { + ASSERT_NE(sock.ssl_get_peer_cert(), ""); + } + + ASSERT_GT(sock.send(SW_STRL(TEST_REQUEST_BAIDU)), 0); + + String buf(65536); + while (true) { + char rbuf[4096]; + ssize_t nr = sock.recv(rbuf, sizeof(rbuf)); + if (nr <= 0) { + break; + } + buf.append(rbuf, nr); + } + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +TEST(coroutine_socket, http_get_with_socks5_proxy) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", 9909); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.listen(128), true); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, false); + }); +} - Socket *conn = sock.accept(); - conn->send("hello world\n"); - conn->send("\r"); - char buf[256]; - memset(buf, 'A', 128); - memset(buf + 128, 'B', 125); - conn->send(buf, 253); - delete conn; - }, - - [](void *arg) { +TEST(coroutine_socket, http_get_with_http_proxy) { + coroutine::run([&](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9909, -1); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.errCode, 0); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, false); + }); +} - size_t n; - auto buf = sock.get_read_buffer(); +TEST(coroutine_socket, https_get_with_socks5_proxy) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, true); + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 12); - ASSERT_MEMEQ(buf->str, "hello world\n", 12); +TEST(coroutine_socket, https_get_with_http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, true); + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 1); - ASSERT_MEMEQ(buf->str, "\r", 1); +#ifdef SW_USE_OPENSSL +TEST(coroutine_socket, ssl) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); - char buf_2[256]; - memset(buf_2, 'A', 128); - memset(buf_2 + 128, 'B', 125); + sock.enable_ssl_encrypt(); + sock.get_ssl_context()->cert_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.crt"; + sock.get_ssl_context()->key_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.key"; + sock.get_ssl_context()->verify_peer = false; + sock.get_ssl_context()->allow_self_signed = true; + sock.get_ssl_context()->cafile = swoole::test::get_root_path() + "/tests/include/ssl_certs/ca.crt"; - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 128); - ASSERT_MEMEQ(buf->str, buf_2, 128); + proxy_test(sock, 443); + }); +} +#endif + +TEST(coroutine_socket, peek) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 125); - ASSERT_MEMEQ(buf->str, buf_2 + 128, 125); + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.peek(data, 5); + sock.close(); + ASSERT_EQ(result, 5); + data[result] = '\0'; + ASSERT_STREQ("Hello", data); + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 0); - }}); +TEST(coroutine_socket, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = sock.sendmsg(&msg, 0); + sock.close(); + ASSERT_EQ(ret, length); + }); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = sock.recvmsg(&msg, 0); + buf[ret] = '\0'; + sock.close(); + ASSERT_STREQ(buf, text.c_str()); + }); +} + +std::pair, std::shared_ptr> create_socket_pair() { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + auto sock0 = new Socket(pairs[0], SW_SOCK_UNIX_STREAM); + auto sock1 = new Socket(pairs[1], SW_SOCK_UNIX_STREAM); + + sock0->get_socket()->set_buffer_size(65536); + sock1->get_socket()->set_buffer_size(65536); + + std::pair, std::shared_ptr> result(sock0, sock1); + return result; +} + +TEST(coroutine_socket, close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = _sock->write(buffer->value(), buffer->get_length()); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + ASSERT_EQ(_sock->write(buffer->value(), buffer->get_length()), -1); + ASSERT_EQ(_sock->errCode, EBADF); + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = _sock->read(buf, sizeof(buf)); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_TRUE(_sock->close()); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(_sock->is_closed()); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, EBADF); + }); +} + +TEST(coroutine_socket, cancel) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = pair.first->read(buf, sizeof(buf)); + if (result < 0 && pair.first->errCode == ECANCELED) { + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + pair.first->cancel(SW_EVENT_READ); + ASSERT_TRUE(results["read"]); + }); } diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 179718f9743..0552a909bfe 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -13,11 +13,12 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ #include "test_coroutine.h" +#include "swoole_pipe.h" using namespace swoole; using namespace swoole::test; @@ -44,20 +45,17 @@ TEST(coroutine_system, file) { } TEST(coroutine_system, flock) { - std::shared_ptr buf = std::make_shared(65536); ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); buf->str[buf->size - 1] = 0; - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - - Coroutine::create([&buf](void*) { + test::coroutine::run([&buf](void *) { int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); ASSERT_TRUE(fd > 0); swoole_coroutine_flock_ex(test_file, fd, LOCK_EX); for (int i = 0; i < 4; i++) { - Coroutine::create([&buf](void*) { + Coroutine::create([&buf](void *) { int fd = swoole_coroutine_open(test_file, File::READ, 0); ASSERT_TRUE(fd > 0); swoole_coroutine_flock_ex(test_file, fd, LOCK_SH); @@ -77,17 +75,206 @@ TEST(coroutine_system, flock) { swoole_coroutine_close(fd); }); - swoole_event_wait(); unlink(test_file); } +TEST(coroutine_system, flock_nb) { + coroutine::run([&](void *arg) { + int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX | LOCK_NB), 0); + + swoole::Coroutine::create([&](void *arg) { + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX), 0); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + swoole_coroutine_close(fd); + unlink(test_file); + }); + + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + }); +} + TEST(coroutine_system, cancel_sleep) { test::coroutine::run([](void *arg) { auto co = Coroutine::get_current_safe(); - Coroutine::create([co](void *){ + Coroutine::create([co](void *) { System::sleep(0.002); co->cancel(); }); System::sleep(1000); }); } + +TEST(coroutine_system, getaddrinfo) { + test::coroutine::run([](void *arg) { + std::vector ip_list = System::getaddrinfo("www.baidu.com", AF_INET, SOCK_STREAM, 0, "http", 1); + ASSERT_GT(ip_list.size(), 0); + for (auto &ip : ip_list) { + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); + } + }); +} + +TEST(coroutine_system, wait_signal) { + test::coroutine::run([](void *arg) { + Coroutine::create([](void *) { + System::sleep(0.002); + kill(getpid(), SIGUSR1); + }); + ASSERT_EQ(System::wait_signal(SIGUSR1, 1.0), SIGUSR1); + ASSERT_EQ(System::wait_signal(SIGUSR2, 0.1), -1); + }); +} + +static const char *GREETING = "hello world, hello swoole"; + +TEST(coroutine_system, wait_event_readable) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + test::coroutine::run([&p](void *arg) { + Coroutine::create([&p](void *) { + System::sleep(0.002); + ASSERT_GT(p.write(GREETING, strlen(GREETING)), 0); + }); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + ssize_t n = pipe_sock->read(buffer, sizeof(buffer)); + buffer[n] = 0; + EXPECT_EQ(strlen(GREETING), n); + EXPECT_STREQ(GREETING, buffer); + }); +} + +TEST(coroutine_system, wait_event_writable) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); + sw_tg_buffer()->clear(); + + String str(2 * SW_NUM_MILLION); + str.append_random_bytes(str.size - 1, false); + str.append('\0'); + + test::coroutine::run([&](void *arg) { + Coroutine::create([&](void *) { + System::sleep(0.002); + auto pipe_sock = p.get_socket(true); + + char *ptr = str.value(); + size_t len = str.get_length(); + + while (len > 0) { + ssize_t retval = pipe_sock->write(ptr, len > 8192 ? 8192 : len); + if (retval > 0) { + ptr += retval; + len -= retval; + } else if (retval == 0 || (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_WRITE, 1); + } + }); + + auto pipe_sock = p.get_socket(false); + auto tg_buf = sw_tg_buffer(); + + while (tg_buf->length < str.size - 1) { + ssize_t retval = pipe_sock->read(tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length); + if (retval > 0) { + tg_buf->grow(retval); + continue; + } else if (retval == 0 && (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + } + tg_buf->append('\0'); + EXPECT_STREQ(sw_tg_buffer()->value(), str.value()); + }); +} + +TEST(coroutine_system, swoole_stream_select) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + test::coroutine::run([&](void *arg) { + // try timeout to trigger socket_poll_timeout function + ASSERT_FALSE(System::socket_poll(fds, 0.5)); + }); + + // start normal process + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + System::sleep(0.05); + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0.5); + ASSERT_TRUE(result); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + ssize_t retval = pipe_sock->read(buffer, sizeof(buffer)); + buffer[retval] = '\0'; + + ASSERT_EQ(retval, len); + const char *ptr = text.c_str(); + ASSERT_STREQ(ptr, buffer); + }); +} + +TEST(coroutine_system, timeout_is_zero) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + // timeout is 0 + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0); + ASSERT_TRUE(result); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + auto pipe_sock = p.get_socket(false); + result = System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 0); + ASSERT_TRUE(result); + }); +} + +TEST(coroutine_system, exec) { + test::coroutine::run([](void *arg) { + int status; + auto buffer = std::shared_ptr(swoole::make_string(1024)); + ASSERT_TRUE(System::exec("ls /", true, buffer, &status)); + ASSERT_TRUE(buffer->contains(SW_STRL("tmp"))); + }); +} diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp index e803fa8ec22..a2da9992ede 100644 --- a/core-tests/src/lock/lock.cpp +++ b/core-tests/src/lock/lock.cpp @@ -13,11 +13,11 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ -#include "test_core.h" +#include "test_coroutine.h" #include "swoole_lock.h" #include "swoole_util.h" @@ -30,6 +30,10 @@ using swoole::RWLock; using swoole::SpinLock; #endif using swoole::Mutex; +using swoole::CoroutineLock; +using swoole::Coroutine; +using swoole::test::coroutine; +using swoole::coroutine::System; static void test_func(swLock &lock) { int count = 0; @@ -52,9 +56,51 @@ static void test_func(swLock &lock) { ASSERT_EQ(count, N * 2); } +static void test_lock_rd_func(swLock &lock) { + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock_rd(), 0); + usleep(2000); // wait + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GE(lock.trylock_rd(), 0); + }); + + t1.join(); + t2.join(); +} + +static void test_share_lock_fun(swLock &lock) { + lock.lock(); + const int sleep_us = 10000; + int magic_num = swoole_rand(100000, 9999999); + int *_num = (int *) sw_mem_pool()->alloc(sizeof(int)); + *_num = 0; + + pid_t pid = fork(); + + if (pid == 0) { + lock.lock(); + *_num = magic_num; + usleep(1); + exit(0); + } else { + usleep(sleep_us); + lock.unlock(); + int status; + pid_t _pid = waitpid(pid, &status, 0); + if (_pid != pid) { + swoole_warning("error pid=%d", _pid); + } + ASSERT_EQ(*_num, magic_num); + } +} + TEST(lock, mutex) { Mutex lock(0); - test_func( reinterpret_cast(lock)); + test_func(reinterpret_cast(lock)); } TEST(lock, lockwait) { @@ -85,44 +131,87 @@ TEST(lock, lockwait) { TEST(lock, shared) { Mutex lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} - lock.lock(); - - const int sleep_us = 10000; - - int magic_num = swoole_rand(100000, 9999999); - int *_num = (int *) sw_mem_pool()->alloc(sizeof(int)); - *_num = 0; +TEST(lock, try_rd) { + Mutex lock(0); + test_lock_rd_func(lock); +} - pid_t pid = fork() ; +TEST(lock, coroutine_lock) { + CoroutineLock *lock = new CoroutineLock(false); + ASSERT_EQ(lock->lock(), SW_ERROR_CO_OUT_OF_COROUTINE); + auto callback = [lock]() { + coroutine::run([lock](void *arg) { + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { ASSERT_EQ(lock->trylock(), EBUSY); }); + }); + }; - if (pid == 0) { - lock.lock(); - *_num = magic_num; - usleep(1); - exit(0); - } else { - usleep(sleep_us); - lock.unlock(); - int status; - pid_t _pid = waitpid(pid, &status, 0); - if (_pid != pid ) { - swoole_warning("error pid=%d", _pid); - } - ASSERT_EQ(*_num, magic_num); - } + std::thread t1(callback); + t1.join(); + delete lock; } #ifdef HAVE_RWLOCK +TEST(lock, rwlock_shared) { + RWLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + TEST(lock, rwlock) { RWLock lock(false); test_func(lock); } + +TEST(lock, rwlock_try_rd) { + RWLock lock(false); + test_lock_rd_func(lock); +} + +TEST(lock, rw_try_wr) { + RWLock lock(false); + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock(), 0); + usleep(2000); + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GT(lock.trylock(), 0); + }); + t1.join(); + t2.join(); +} #endif #ifdef HAVE_SPINLOCK +TEST(lock, spinlock_shared) { + SpinLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + TEST(lock, spinlock) { SpinLock lock(false); test_func(lock); } + +TEST(lock, spinlock_try_rd) { + SpinLock lock(false); + test_lock_rd_func(lock); +} #endif diff --git a/core-tests/src/main.cpp b/core-tests/src/main.cpp index 31b96503eeb..05f8a5c6b36 100644 --- a/core-tests/src/main.cpp +++ b/core-tests/src/main.cpp @@ -1,5 +1,7 @@ #include "test_core.h" +#include "swoole_proxy.h" + using namespace swoole; using namespace std; @@ -25,8 +27,13 @@ int main(int argc, char **argv) { static void init_root_path(const char *_exec_file) { char buf[PATH_MAX]; - char *dir = getcwd(buf, sizeof(buf)); - string file = string(dir) + "/" + _exec_file; + string file; + if (_exec_file[0] == '/') { + file = _exec_file; + } else { + char *dir = getcwd(buf, sizeof(buf)); + file = string(dir) + "/" + _exec_file; + } string relative_root_path = file.substr(0, file.rfind('/')) + "/../../"; char *_realpath = realpath(relative_root_path.c_str(), buf); if (_realpath == nullptr) { @@ -51,5 +58,33 @@ bool is_github_ci() { return getenv("GITHUB_ACTIONS") != nullptr; } +Socks5Proxy *create_socks5_proxy() { + auto socks5_proxy = new Socks5Proxy(); + socks5_proxy->host = std::string(TEST_SOCKS5_PROXY_HOST); + socks5_proxy->port = TEST_SOCKS5_PROXY_PORT; + socks5_proxy->dns_tunnel = 1; + if (is_github_ci()) { + socks5_proxy->method = SW_SOCKS5_METHOD_AUTH; + socks5_proxy->username = std::string(TEST_SOCKS5_PROXY_USER); + socks5_proxy->password = std::string(TEST_SOCKS5_PROXY_PASSWORD); + } + return socks5_proxy; +} + +HttpProxy *create_http_proxy() { + auto http_proxy = new HttpProxy(); + http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); + http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; + if (is_github_ci()) { + http_proxy->username = std::string(TEST_HTTP_PROXY_USER); + http_proxy->password = std::string(TEST_HTTP_PROXY_PASSWORD); + } + return http_proxy; +} + +int get_random_port() { + return TEST_PORT + swoole_system_random(1, 10000); +} + } // namespace test } // namespace swoole diff --git a/core-tests/src/memory/buffer.cpp b/core-tests/src/memory/buffer.cpp index e617e0e8e17..101c7f6f86d 100644 --- a/core-tests/src/memory/buffer.cpp +++ b/core-tests/src/memory/buffer.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,14 +26,16 @@ using namespace swoole; TEST(buffer, append_iov) { Buffer buf(1024); + Buffer buf_for_offset(1024); int iovcnt = 4; iovec v[iovcnt]; + size_t total_len = 0; - v[0].iov_len = swoole_rand(99, 4095); - v[1].iov_len = swoole_rand(99, 4095); - v[2].iov_len = swoole_rand(99, 4095); - v[3].iov_len = swoole_rand(99, 4095); + SW_LOOP_N (iovcnt) { + v[i].iov_len = swoole_rand(99, 4095); + total_len += v[i].iov_len; + } unique_ptr s1(new char[v[0].iov_len]); unique_ptr s2(new char[v[1].iov_len]); @@ -51,21 +53,29 @@ TEST(buffer, append_iov) { memset(v[3].iov_base, 'D', v[3].iov_len); buf.append(v, iovcnt, 0); + ASSERT_EQ(buf.length(), total_len); - ASSERT_EQ(buf.length(), v[0].iov_len + v[1].iov_len + v[2].iov_len+ v[3].iov_len); + size_t offset = swoole_rand(v[0].iov_len + 1, total_len - 1); + buf_for_offset.append(v, iovcnt, offset); + ASSERT_EQ(buf_for_offset.length(), total_len - offset); - String str(buf.length()); - - while(!buf.empty()) { - auto chunk = buf.front(); - str.append(chunk->value.ptr, chunk->length); - buf.pop(); - } + String str(buf_for_offset.length()); - size_t offset = 0; + while (!buf_for_offset.empty()) { + auto chunk = buf_for_offset.front(); + str.append(chunk->value.str, chunk->length); + buf_for_offset.pop(); + } + size_t indent = 0; SW_LOOP_N (iovcnt) { - ASSERT_EQ(memcmp(str.str + offset, v[i].iov_base, v[i].iov_len), 0); - offset += v[i].iov_len; + if (offset >= v[i].iov_len) { + offset -= v[i].iov_len; + continue; + } + + ASSERT_EQ(memcmp(str.str + indent, (char *) v[i].iov_base + offset, v[i].iov_len - offset), 0); + indent += v[i].iov_len - offset; + offset = 0; } } diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp index f6f67e6c302..2a4a21cb8e5 100644 --- a/core-tests/src/memory/fixed_pool.cpp +++ b/core-tests/src/memory/fixed_pool.cpp @@ -13,12 +13,13 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ #include "test_core.h" #include "swoole_memory.h" +#include "swoole_util.h" using namespace std; @@ -26,18 +27,37 @@ TEST(fixed_pool, alloc) { auto *pool = new swoole::FixedPool(1024, 256, false); list alloc_list; + ASSERT_EQ(pool->get_slice_size(), 256); for (int i = 0; i < 1200; i++) { int j = rand(); + void *mem; + if (j % 4 < 3) { - void *mem = pool->alloc(0); + mem = pool->alloc(0); ASSERT_TRUE(mem); alloc_list.push_back(mem); } else if (!alloc_list.empty()) { - void *mem = alloc_list.front(); + if (j % 2 == 1) { + mem = alloc_list.front(); + alloc_list.pop_front(); + } else { + mem = alloc_list.back(); + alloc_list.pop_back(); + } pool->free(mem); } } - + pool->debug(1); delete pool; } + +TEST(fixed_pool, realloc) { + void *memory = sw_shm_malloc(1024); + void *new_memory = sw_shm_realloc(memory, 2048); + ON_SCOPE_EXIT { + sw_shm_free(new_memory); + }; + ASSERT_NE(new_memory, nullptr); + +} diff --git a/core-tests/src/memory/global_memory.cpp b/core-tests/src/memory/global_memory.cpp index 1f50a644f6b..d573caba3a6 100644 --- a/core-tests/src/memory/global_memory.cpp +++ b/core-tests/src/memory/global_memory.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -39,6 +39,7 @@ TEST(global_memory, alloc) { ASSERT_TRUE(ptr3); ASSERT_GT(pool->capacity(), 2 * 1024 * 1024 - 512); + ASSERT_GT(pool->get_memory_size(), 0); ASSERT_STREQ(ptr1, "hello, world, #1"); ASSERT_STREQ(ptr2, "hello, world, #2"); diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp index a7d9af58569..d58ca0b0ac2 100644 --- a/core-tests/src/memory/table.cpp +++ b/core-tests/src/memory/table.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -150,14 +150,7 @@ TEST(table, create) { ASSERT_FALSE(table.exists("php")); } -TEST(table, iterator) { - table_t table(1024); - - table.set("php", {"php", 1, 1.245}); - table.set("java", {"java", 2, 3.1415926}); - table.set("c++", {"c++", 3, 4.888}); - - auto _ptr = table.ptr(); +void start_iterator(Table *_ptr) { _ptr->rewind(); auto count = 0; while (true) { @@ -171,3 +164,169 @@ TEST(table, iterator) { } ASSERT_EQ(count, _ptr->count()); } + +TEST(table, iterator) { + table_t table(1024); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + auto _ptr = table.ptr(); + start_iterator(_ptr); +} + +TEST(table, iterator_2) { + table_t table(1024); + auto _ptr = table.ptr(); + _ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + start_iterator(_ptr); +} + +static int test_table_size = 128; + +static void create_table(table_t &table) { + auto ptr = table.ptr(); + ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + ASSERT_TRUE(table.set("php", {"php", 1, 1.245})); + ASSERT_TRUE(table.set("java", {"java", 2, 3.1415926})); + ASSERT_TRUE(table.set("c++", {"c++", 3, 4.888})); + ASSERT_TRUE(table.set("js", {"js", 9, 6565})); + ASSERT_TRUE(table.set("golang", {"golang", 4, 9.888})); +} + +TEST(table, conflict1) { + table_t table(test_table_size); + ASSERT_FALSE(table.exists("swift")); + + create_table(table); + auto ptr = table.ptr(); + + ASSERT_FALSE(table.exists("kotlin")); + + ASSERT_TRUE(table.del("php")); + ASSERT_FALSE(table.exists("php")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict2) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict3) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict4) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("c++")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, get_value) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + _rowlock->unlock(); + TableColumn *column_id = ptr->get_column("id"); + TableColumn *column_name = ptr->get_column("name"); + TableColumn *column_score = ptr->get_column("score"); + + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + double dval = 0; + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 1.245); + + long lval = 0; + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 1); + + column_id->clear(row); + column_name->clear(row); + column_score->clear(row); + + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 0); + + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 0); +} + +TEST(table, lock) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + + for (int i = 0; i <= 3; i++) { + std::thread t([&]() { + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + TableColumn *column_name = ptr->get_column("name"); + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + }); + t.join(); + } + _rowlock->unlock(); +} diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp index 2447ea45e28..acbee375417 100644 --- a/core-tests/src/network/client.cpp +++ b/core-tests/src/network/client.cpp @@ -5,24 +5,30 @@ #define GREETER "Hello Swoole" #define GREETER_SIZE sizeof(GREETER) +using swoole::HttpProxy; +using swoole::Pipe; +using swoole::Socks5Proxy; +using swoole::String; using swoole::network::AsyncClient; using swoole::network::Client; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; using swoole::test::Process; using swoole::test::Server; -using swoole::Pipe; TEST(client, tcp) { int ret; char buf[128]; pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -33,7 +39,7 @@ TEST(client, tcp) { Client cli(SW_SOCK_TCP, false); ASSERT_NE(cli.socket, nullptr); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); ASSERT_EQ(ret, 0); ret = cli.send(&cli, SW_STRS(GREETER), 0); ASSERT_GT(ret, 0); @@ -49,16 +55,16 @@ TEST(client, tcp) { TEST(client, udp) { int ret; char buf[128]; - + int port = swoole::test::get_random_port(); pid_t pid; - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_packet_lambda_type packet_fn = [](ON_PACKET_PARAMS) { swoole::DgramPacket *packet = (swoole::DgramPacket *) req->data; SERVER_THIS->sendto(packet->socket_addr, packet->data, packet->length, req->info.server_fd); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_UDP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_UDP); serv.on("onPacket", (void *) packet_fn); serv.start(); }); @@ -69,7 +75,7 @@ TEST(client, udp) { Client cli(SW_SOCK_UDP, false); ASSERT_NE(cli.socket, nullptr); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); ASSERT_EQ(ret, 0); ret = cli.send(&cli, SW_STRS(GREETER), 0); ASSERT_GT(ret, 0); @@ -82,18 +88,18 @@ TEST(client, udp) { wait(&status); } -TEST(client, async_tcp) { +static void test_async_client_tcp(const char *host, int port) { pid_t pid; Pipe p(true); ASSERT_TRUE(p.ready()); - Process proc([&p](Process *proc) { + Process proc([&p, port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.set_private_data("pipe", &p); @@ -134,7 +140,7 @@ TEST(client, async_tcp) { ac->close(); }); - bool retval = ac.connect(TEST_HOST, TEST_PORT); + bool retval = ac.connect(host, port); EXPECT_TRUE(retval); swoole_event_wait(); @@ -144,10 +150,18 @@ TEST(client, async_tcp) { wait(&status); } +TEST(client, async_tcp) { + test_async_client_tcp(TEST_HOST, swoole::test::get_random_port()); +} + +TEST(client, async_tcp_dns) { + test_async_client_tcp("localhost", swoole::test::get_random_port()); +} + TEST(client, connect_refuse) { int ret; Client cli(SW_SOCK_TCP, false); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT + 10001, -1, 0); + ret = cli.connect(&cli, TEST_HOST, swoole::test::get_random_port(), -1, 0); ASSERT_EQ(ret, -1); ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED); } @@ -155,7 +169,7 @@ TEST(client, connect_refuse) { TEST(client, connect_timeout) { int ret; Client cli(SW_SOCK_TCP, false); - ret = cli.connect(&cli, "19.168.0.99", TEST_PORT + 10001, 0.2, 0); + ret = cli.connect(&cli, "19.168.0.99", swoole::test::get_random_port(), 0.2, 0); ASSERT_EQ(ret, -1); ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT); } @@ -207,12 +221,11 @@ TEST(client, shutdown_all) { } #ifdef SW_USE_OPENSSL -TEST(client, ssl_1) { - int ret; +TEST(client, ssl_1) { bool connected = false; bool closed = false; - swoole::String buf(65536); + String buf(65536); swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); @@ -220,23 +233,14 @@ TEST(client, ssl_1) { client.enable_ssl_encrypt(); client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), 0); + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); }; - client.onError = [](Client *cli) { - }; - client.onClose = [&closed](Client *cli) { - closed = true; - }; - client.onReceive = [&buf](Client *cli, const char *data, size_t length) { - buf.append(data, length); - }; - ret = client.connect(&client, "www.baidu.com", 443, -1, 0); - ASSERT_EQ(ret, 0); + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, 443, -1, 0), 0); swoole_event_wait(); @@ -245,49 +249,109 @@ TEST(client, ssl_1) { ASSERT_TRUE(buf.contains("Baidu")); } -TEST(client, http_proxy) { - // skip in github action - if (swoole::test::is_github_ci()) { - return; - } - - int ret; +static void proxy_async_test(Client &client, bool https) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); bool connected = false; bool closed = false; - swoole::String buf(65536); - - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + String buf(65536); - Client client(SW_SOCK_TCP, true); - client.enable_ssl_encrypt(); - client.set_http_proxy(TEST_HTTP_PROXY_HOST, TEST_HTTP_PROXY_PORT); + if (https) { + client.enable_ssl_encrypt(); + } client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), 0); + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); }; - client.onError = [](Client *cli) { - }; - client.onClose = [&closed](Client *cli) { - closed = true; - }; - client.onReceive = [&buf](Client *cli, const char *data, size_t length) { - buf.append(data, length); - }; - ret = client.connect(&client, "www.baidu.com", 443, -1, 0); - ASSERT_EQ(ret, 0); + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); swoole_event_wait(); ASSERT_TRUE(connected); ASSERT_TRUE(closed); - ASSERT_TRUE(buf.contains("Baidu")); + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +static void proxy_sync_test(Client &client, bool https) { + String buf(65536); + if (https) { + client.enable_ssl_encrypt(); + } + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); + ASSERT_GT(client.send(&client, SW_STRL(TEST_REQUEST_BAIDU), 0), 0); + + while (true) { + char rbuf[4096]; + auto nr = client.recv(&client, rbuf, sizeof(rbuf), 0); + if (nr <= 0) { + break; + } + buf.append(rbuf, nr); + } + + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +static void proxy_set_socks5_proxy(Client &client) { + client.socks5_proxy = create_socks5_proxy(); +} + +static void proxy_set_http_proxy(Client &client) { + client.http_proxy = create_http_proxy(); +} + +TEST(client, https_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, true); +} + +TEST(client, https_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, true); +} + +TEST(client, https_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, https_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, http_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, false); +} + +TEST(client, http_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, false); } -#endif +TEST(client, http_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, false); +} + +TEST(client, http_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, false); +} +#endif diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index 664d3c799e8..f89746600c8 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -74,14 +74,7 @@ TEST(dns, cancel) { } TEST(dns, getaddrinfo) { - char buf[1024] = {}; - swoole::network::GetaddrinfoRequest req = {}; - req.hostname = "www.baidu.com"; - req.family = AF_INET; - req.socktype = SOCK_STREAM; - req.protocol = 0; - req.service = nullptr; - req.result = buf; + swoole::GetaddrinfoRequest req("www.baidu.com", AF_INET, SOCK_STREAM, 0, ""); ASSERT_EQ(swoole::network::getaddrinfo(&req), 0); ASSERT_GT(req.count, 0); @@ -94,8 +87,30 @@ TEST(dns, getaddrinfo) { } TEST(dns, load_resolv_conf) { - ASSERT_TRUE(swoole_load_resolv_conf()); + // reset + SwooleG.dns_server_host = ""; + SwooleG.dns_server_port = 0; + int port = swoole::test::get_random_port(); + auto dns_server = swoole_get_dns_server(); + ASSERT_TRUE(dns_server.first.empty()); + ASSERT_EQ(dns_server.second, 0); + + // with port + std::string test_server = "127.0.0.1:" + std::to_string(port); // fake dns server + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_STREQ(dns_server.first.c_str(), "127.0.0.1"); + ASSERT_EQ(dns_server.second, port); + + // invalid port + test_server = "127.0.0.1:808088"; + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_EQ(dns_server.second, SW_DNS_SERVER_PORT); + + ASSERT_TRUE(swoole_load_resolv_conf()); + dns_server = swoole_get_dns_server(); ASSERT_FALSE(dns_server.first.empty()); ASSERT_NE(dns_server.second, 0); } @@ -139,3 +154,59 @@ TEST(dns, gethosts) { ip = swoole::coroutine::get_ip_by_hosts("non.exist.com"); ASSERT_EQ(ip, ""); } + +void name_resolver_test_fn_1() { + NameResolver::Context ctx{}; + ctx.type = AF_INET; + ctx.timeout = 1; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); +} + +void name_resolver_test_fn_2() { + NameResolver::Context ctx; + std::string domain = "non.exist.com"; + NameResolver nr{[](const std::string &domain, NameResolver::Context *ctx, void *) -> std::string { + if (domain == "name1") { + return "127.0.0.2"; + } else if (domain == "www.baidu.com") { + ctx->final_ = true; + return ""; + } + return ""; + }, + nullptr, + NameResolver::TYPE_USER}; + + swoole_name_resolver_add(nr); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.2", swoole_name_resolver_lookup("name1", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("", swoole_name_resolver_lookup("www.baidu.com", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); + + swoole_name_resolver_each([](const std::list::iterator &iter) -> swTraverseOperation { + if (iter->type == NameResolver::TYPE_USER) { + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + + ctx = {AF_INET}; + auto ip = swoole_name_resolver_lookup("www.baidu.com", &ctx); + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); +} + +TEST(dns, name_resolver_1) { + name_resolver_test_fn_1(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_1(); }); +} + +TEST(dns, name_resolver_2) { + name_resolver_test_fn_2(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_2(); }); +} diff --git a/core-tests/src/network/socket.cpp b/core-tests/src/network/socket.cpp index d3888dafb56..f4b656eb96e 100644 --- a/core-tests/src/network/socket.cpp +++ b/core-tests/src/network/socket.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -89,9 +89,10 @@ TEST(socket, sendto_ipv6) { TEST(socket, recv) { mutex m; m.lock(); + int port = swoole::test::get_random_port(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); char buf[1024] = {}; svr->set_block(); m.unlock(); @@ -103,10 +104,10 @@ TEST(socket, recv) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); - ASSERT_EQ(cli->connect(TEST_HOST, TEST_PORT), SW_OK); + ASSERT_EQ(cli->connect(TEST_HOST, port), SW_OK); std::this_thread::sleep_for(std::chrono::milliseconds(100)); cli->send(test_data, sizeof(test_data), 0); cli->free(); @@ -119,9 +120,10 @@ TEST(socket, recv) { TEST(socket, recvfrom_blocking) { mutex m; m.lock(); + int port = swoole::test::get_random_port(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, TEST_PORT); + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, port); network::Address addr; char buf[1024] = {}; svr->set_nonblock(); @@ -131,11 +133,11 @@ TEST(socket, recvfrom_blocking) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, port]() { m.lock(); auto cli = make_socket(SW_SOCK_UDP, SW_FD_STREAM_CLIENT, 0); network::Address addr; - addr.assign(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + addr.assign(SW_SOCK_TCP, TEST_HOST, port); ASSERT_EQ(cli->connect(addr), SW_OK); std::this_thread::sleep_for(std::chrono::milliseconds(100)); cli->send(test_data, sizeof(test_data), 0); @@ -149,12 +151,13 @@ TEST(socket, recvfrom_blocking) { TEST(socket, sendfile_blocking) { string file = test::get_root_path() + "/examples/test.jpg"; mutex m; + int port = swoole::test::get_random_port(); m.lock(); auto str = file_get_contents(file); - thread t1([&m, &str]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, &str, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); m.unlock(); auto cli = svr->accept(); int len; @@ -169,11 +172,11 @@ TEST(socket, sendfile_blocking) { svr->free(); }); - thread t2([&m, &file, &str]() { + thread t2([&m, &file, &str, port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); network::Address addr; - addr.assign(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + addr.assign(SW_SOCK_TCP, TEST_HOST, port); ASSERT_EQ(cli->connect(addr), SW_OK); int len = htonl(str->get_length()); cli->send(&len, sizeof(len), 0); @@ -291,10 +294,11 @@ TEST(socket, clean) { TEST(socket, check_liveness) { mutex m; + int svr_port = swoole::test::get_random_port(); m.lock(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, svr_port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, svr_port); m.unlock(); auto cli = svr->accept(); @@ -313,11 +317,11 @@ TEST(socket, check_liveness) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, svr_port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); - ASSERT_EQ(cli->connect(TEST_HOST, TEST_PORT), SW_OK); + ASSERT_EQ(cli->connect(TEST_HOST, svr_port), SW_OK); cli->send(test_data, sizeof(test_data), 0); std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -333,3 +337,46 @@ TEST(socket, check_liveness) { t1.join(); t2.join(); } + +#define CRLF "\r\n" + +TEST(socket, sync) { + auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0); + + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://httpbin.org:80")); + + ASSERT_EQ(sock->connect(addr), 0); + + const char *req = "GET / HTTP/1.1" CRLF \ + "Host: httpbin.org" CRLF \ + "User-Agent: curl/7.81.0" CRLF \ + "Accept: */*" CRLF \ + "Connection: close" CRLF \ + CRLF CRLF; + ssize_t n = strlen(req); + ASSERT_EQ(sock->write_sync(req, n), n); + + string resp; + SW_LOOP { + char buf[1024]; + n = sock->read_sync(buf, sizeof(buf)); + if (n == 0) { + break; + } + ASSERT_GT(n, 0); + resp.append(buf, n); + } + + ASSERT_GT(resp.length(), 4096); + sock->free(); +} + +TEST(socket, ipv6_addr) { + auto sock = make_socket(SW_SOCK_TCP6, SW_FD_STREAM, 0); + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://[::1]:12345")); + ASSERT_EQ(sock->connect(addr), SW_ERR); + ASSERT_EQ(errno, ECONNREFUSED); + sock->free(); +} diff --git a/core-tests/src/network/stream.cpp b/core-tests/src/network/stream.cpp index 7fa068e4a3e..5dd870eefa0 100644 --- a/core-tests/src/network/stream.cpp +++ b/core-tests/src/network/stream.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -21,15 +21,17 @@ #include "swoole_server.h" using namespace std; +using namespace swoole; using namespace swoole::network; TEST(stream, send) { - swServer serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; + int svr_port = swoole::test::get_random_port(); int ori_log_level = sw_logger()->get_level(); sw_logger()->set_level(SW_LOG_ERROR); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, svr_port); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -67,7 +69,7 @@ TEST(stream, send) { ASSERT_EQ(stream1->send(buf, sizeof(buf)), SW_OK); // success requset - auto stream2 = Stream::create(TEST_HOST, TEST_PORT, SW_SOCK_TCP); + auto stream2 = Stream::create(TEST_HOST, svr_port, SW_SOCK_TCP); ASSERT_TRUE(stream2); stream2->private_data = new string(buf, sizeof(buf)); stream2->response = [](Stream *stream, const char *data, uint32_t length) { @@ -83,9 +85,9 @@ TEST(stream, send) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [&buf](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [&buf](Server *serv, RecvData *req) -> int { string req_body(req->data + 4, req->info.len - 4); EXPECT_EQ(string(buf, sizeof(buf)), req_body); diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index 9a0e514548c..8a122069db7 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -78,10 +78,16 @@ TEST(async, schedule) { count--; if (count == 0) { swoole_timer_del(timer); - ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 128); + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 16); ASSERT_GT(SwooleTG.async_threads->get_queue_size(), 100); ASSERT_GT(SwooleTG.async_threads->get_task_num(), 100); break; + } else if (count == N - 1) { + ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 4); + ASSERT_LE(SwooleTG.async_threads->get_queue_size(), 1); + ASSERT_EQ(SwooleTG.async_threads->get_task_num(), 1); + } else if (count < N / 2) { + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 4); } } }); diff --git a/core-tests/src/os/file.cpp b/core-tests/src/os/file.cpp new file mode 100644 index 00000000000..1b85b01cbc4 --- /dev/null +++ b/core-tests/src/os/file.cpp @@ -0,0 +1,47 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_file.h" + +using namespace swoole; + +TEST(file, read_line) { + std::string filename = test::get_root_path() + "/tests/include/bootstrap.php"; + File file(filename, File::READ); + FILE *stdc_file = fopen(filename.c_str(), "r"); + ASSERT_NE(stdc_file, nullptr); + char buf1[1024]; + char buf2[1024]; + + size_t size = file.get_size(); + size_t total = 0; + + while(true) { + auto retval = file.read_line(buf1, sizeof(buf1)); + if (retval == 0) { + break; + } + total += retval; + ASSERT_NE(fgets(buf2, sizeof(buf2), stdc_file), nullptr); + ASSERT_STREQ(buf1, buf2); + } + ASSERT_EQ(total, size); +} diff --git a/core-tests/src/os/msg_queue.cpp b/core-tests/src/os/msg_queue.cpp index fb8198941bd..31a7a72f755 100644 --- a/core-tests/src/os/msg_queue.cpp +++ b/core-tests/src/os/msg_queue.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 87413f3c8a2..5f043f7a431 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -7,101 +7,156 @@ #define sysv_signal signal #endif +#include "swoole_signal.h" + using namespace swoole; static void test_func(ProcessPool &pool) { EventData data{}; - data.info.len = strlen(TEST_JPG_MD5SUM); - strcpy(data.data, TEST_JPG_MD5SUM); + size_t size = swoole_system_random(1024, 4096); + String rmem(size); + rmem.append_random_bytes(size - 1); + rmem.append("\0"); + + data.info.len = size; + memcpy(data.data, rmem.value(), size); int worker_id = -1; ASSERT_EQ(pool.dispatch_blocking(&data, &worker_id), SW_OK); pool.running = true; - pool.onTask = [](ProcessPool *pool, EventData *task) -> int { + pool.ptr = &rmem; + pool.main_loop(&pool, pool.get_worker(0)); + pool.destroy(); +} + +static void test_func_task_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_TASK); + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { pool->running = false; - EXPECT_MEMEQ(task->data, TEST_JPG_MD5SUM, task->info.len); + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, task->data, task->len()); return 0; }; - pool.main_loop(&pool, pool.get_worker(0)); - pool.destroy(); + test_func(pool); } -TEST(process_pool, tcp) { +static void test_func_message_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_MESSAGE); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); + }; + test_func(pool); +} + +static void test_func_stream_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_STREAM); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + EventData *msg = (EventData *) rdata->data; + usleep(10000); + EXPECT_MEMEQ(_data->str, msg->data, msg->len()); + }; + test_func(pool); +} + +TEST(process_pool, tcp) { ProcessPool pool{}; + int svr_port = swoole::test::get_random_port(); ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); - ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); - - test_func(pool); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); + + test_func_task_protocol(pool); } -TEST(process_pool, unix_sock) { +TEST(process_pool, unix_sock) { ProcessPool pool{}; signal(SIGPIPE, SIG_IGN); ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); - test_func(pool); + test_func_task_protocol(pool); } -TEST(process_pool, tcp_raw) { +TEST(process_pool, tcp_raw) { ProcessPool pool{}; - constexpr int size = 2*1024*1024; + constexpr int size = 2 * 1024 * 1024; + int svr_port = swoole::test::get_random_port(); ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); - ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); - pool.set_protocol(0, size); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); + pool.set_max_packet_size(size); + pool.set_protocol(SW_PROTOCOL_STREAM); String data(size); - data.append_random_bytes(size-1); + data.append_random_bytes(size - 1); data.append("\0"); - + ASSERT_EQ(pool.dispatch_blocking(data.str, data.length), SW_OK); pool.running = true; pool.ptr = &data; - pool.onMessage = [](ProcessPool *pool, const char *recv_data, uint32_t len) -> void { + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) -> void { pool->running = false; String *_data = (String *) pool->ptr; - EXPECT_MEMEQ(_data->str, recv_data, len); + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); }; pool.main_loop(&pool, pool.get_worker(0)); pool.destroy(); } -TEST(process_pool, msgqueue) { +TEST(process_pool, msgqueue) { ProcessPool pool{}; ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); - test_func(pool); + test_func_task_protocol(pool); +} + +TEST(process_pool, message_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_message_protocol(pool); +} + +TEST(process_pool, stream_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_stream_protocol(pool); } constexpr int magic_number = 99900011; static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; -TEST(process_pool, shutdown) { +TEST(process_pool, shutdown) { ProcessPool pool{}; - int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); - // init - pool.set_protocol(1, 8192); + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); pool.ptr = shm_value; - pool.onWorkerStart = [](ProcessPool *pool, int worker_id) { + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { int *shm_value = (int *) pool->ptr; *shm_value = magic_number; usleep(1); }; - pool.onTask = [](ProcessPool *pool, EventData *task) -> int { + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { kill(pool->master_pid, SIGTERM); return 0; }; current_pool = &pool; - sysv_signal(SIGTERM, [](int sig) { - current_pool->running = false; - }); - + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + // start ASSERT_EQ(pool.start(), SW_OK); @@ -117,6 +172,59 @@ TEST(process_pool, shutdown) { // shutdown pool.shutdown(); pool.destroy(); - + ASSERT_EQ(*shm_value, magic_number); } + +TEST(process_pool, async) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + pool.ptr = shm_value; + pool.async = true; + + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + int *shm_value = (int *) pool->ptr; + *shm_value = magic_number; + current_worker = worker; + + swoole_signal_set(SIGTERM, [](int sig) { + int *shm_value = (int *) current_pool->ptr; + (*shm_value)++; + current_pool->stop(current_worker); + }); + + usleep(10); + }; + + pool.onMessage = [](ProcessPool *pool, RecvData *msg) { + int *shm_value = (int *) pool->ptr; + (*shm_value)++; + kill(pool->master_pid, SIGTERM); + }; + + current_pool = &pool; + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + + // start + ASSERT_EQ(pool.start(), SW_OK); + + EventData msg{}; + msg.info.len = 128; + swoole_random_string(msg.data, msg.info.len); + int worker_id = -1; + pool.dispatch_blocking(&msg, &worker_id); + + // wait + ASSERT_EQ(pool.wait(), SW_OK); + + // shutdown + pool.shutdown(); + pool.destroy(); + + ASSERT_EQ(*shm_value, magic_number + 2); +} diff --git a/core-tests/src/os/signal.cpp b/core-tests/src/os/signal.cpp index 46bb5b00956..a2f2527a14a 100644 --- a/core-tests/src/os/signal.cpp +++ b/core-tests/src/os/signal.cpp @@ -4,12 +4,10 @@ #ifdef HAVE_SIGNALFD static void sig_usr1(int signo) {} -TEST(os_signal, swSignalfd_set) { +TEST(os_signal, signalfd) { int ret; sigset_t curset; - SwooleG.use_signalfd = 1; - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); sigemptyset(&curset); @@ -19,7 +17,6 @@ TEST(os_signal, swSignalfd_set) { swoole_signalfd_init(); swoole_signal_set(SIGUSR1, sig_usr1); - swoole_signalfd_setup(SwooleTG.reactor); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp index aca79a0ea21..98dfdef8d80 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -13,18 +13,18 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ #include "test_core.h" #include "swoole_util.h" +#include "swoole_timer.h" using swoole::Timer; using swoole::TimerNode; TEST(timer, sys) { - SwooleG.use_signalfd = 0; int timer1_count = 0; int timer2_count = 0; int timer_running = true; @@ -32,10 +32,10 @@ TEST(timer, sys) { uint64_t ms1 = swoole::time(); swoole_timer_add( - 20, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); + 20L, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); swoole_timer_add( - 100, + 100L, true, [&](Timer *, TimerNode *tnode) { timer2_count++; @@ -70,7 +70,7 @@ TEST(timer, async) { int timer2_count = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - + uint64_t ms1 = swoole::time(); swoole_timer_after( 20, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); @@ -91,3 +91,62 @@ TEST(timer, async) { ASSERT_EQ(timer1_count, 1); ASSERT_EQ(timer2_count, 5); } + +TEST(timer, exists) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + ASSERT_TRUE(swoole_timer_exists(timer_id)); +} + +TEST(timer, clear) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + swoole_timer_clear(timer_id); + ASSERT_FALSE(swoole_timer_exists(timer_id)); +} + +TEST(timer, get) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + ASSERT_EQ(timerNode->id, timer_id); +} + +TEST(timer, delay) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + uint64_t ms1 = swoole::time(); + uint64_t ms2 = 0; + long timer_id = swoole_timer_after( + 100, [&](Timer *, TimerNode *tnode) { ms2 = swoole::time(); }, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + swoole_timer_delay(timerNode, 100); + swoole_event_wait(); + ASSERT_GE(ms2 - ms1, 100); +} + +TEST(timer, error) { + Timer *tmp = SwooleTG.timer; + SwooleTG.timer = nullptr; + + swoole_timer_free(); + ASSERT_EQ(swoole_timer_select(), SW_ERR); + ASSERT_EQ(swoole_timer_get(1), nullptr); + ASSERT_FALSE(swoole_timer_clear(1)); + ASSERT_FALSE(swoole_timer_exists(1)); + + long timer_id = swoole_timer_tick( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + timer_id = swoole_timer_after( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + swoole_timer_delay(nullptr, 100); + ASSERT_FALSE(swoole_timer_del(nullptr)); + SwooleTG.timer = tmp; +} diff --git a/core-tests/src/os/wait.cpp b/core-tests/src/os/wait.cpp index 431c30c5682..84e94689b8a 100644 --- a/core-tests/src/os/wait.cpp +++ b/core-tests/src/os/wait.cpp @@ -2,17 +2,34 @@ using namespace swoole; using namespace swoole::test; +using swoole::coroutine::System; -TEST(os_wait, waitpid_before_child_exit) { - test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); +static pid_t fork_child() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); - if (pid == 0) { - usleep(100000); - exit(0); - } + if (pid == 0) { + usleep(100000); + exit(0); + } + return pid; +} + +static pid_t fork_child2() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); + if (pid == 0) { + exit(0); + } + + usleep(100000); + return pid; +} + +TEST(os_wait, waitpid_before_child_exit) { + test::coroutine::run([](void *arg) { + auto pid = fork_child(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -22,14 +39,7 @@ TEST(os_wait, waitpid_before_child_exit) { TEST(os_wait, waitpid_after_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - exit(0); - } - - usleep(100000); + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -39,14 +49,7 @@ TEST(os_wait, waitpid_after_child_exit) { TEST(os_wait, wait_before_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - usleep(100000); - exit(0); - } - + pid_t pid = fork_child(); int status = -1; pid_t pid2 = -1; @@ -63,14 +66,7 @@ TEST(os_wait, wait_before_child_exit) { TEST(os_wait, wait_after_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - exit(0); - } - - usleep(100000); + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = -1; @@ -84,3 +80,14 @@ TEST(os_wait, wait_after_child_exit) { ASSERT_EQ(WEXITSTATUS(status), 0); }); } + +TEST(os_wait, waitpid_safe) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); + int status = -1; + + pid_t pid2 = System::waitpid_safe(pid, &status, 0); + ASSERT_EQ(pid2, pid); + ASSERT_EQ(WEXITSTATUS(status), 0); + }); +} diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp index c8feacc49bc..d815e87b551 100644 --- a/core-tests/src/protocol/base.cpp +++ b/core-tests/src/protocol/base.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -61,11 +61,11 @@ TEST(protocol, eof) { } }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; int recv_count = 0; - serv.onReceive = [&](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [&](Server *serv, RecvData *req) -> int { // printf("[1]LEN=%d, count=%d\n%s\n---------------------------------\n", req->info.len, recv_count, // req->data); printf("[2]LEN=%d\n%s\n---------------------------------\n", pkgs[recv_count].length, // pkgs[recv_count].str); @@ -75,7 +75,7 @@ TEST(protocol, eof) { recv_count++; if (recv_count == PKG_N) { - kill(serv->gs->master_pid, SIGTERM); + kill(serv->get_master_pid(), SIGTERM); } return SW_OK; diff --git a/core-tests/src/protocol/base64.cpp b/core-tests/src/protocol/base64.cpp index 097dfed1c46..3328a3e66b1 100644 --- a/core-tests/src/protocol/base64.cpp +++ b/core-tests/src/protocol/base64.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/http2.cpp b/core-tests/src/protocol/http2.cpp new file mode 100644 index 00000000000..e1efda249fe --- /dev/null +++ b/core-tests/src/protocol/http2.cpp @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_coroutine.h" +#include "redis_client.h" +#include "swoole_http2.h" + +using namespace swoole; +using namespace std; + +const std::string REDIS_TEST_KEY = "key-swoole"; +const std::string REDIS_TEST_VALUE = "value-swoole"; + +TEST(http2, default_settings) { + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), SW_HTTP2_DEFAULT_ENABLE_PUSH); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), + SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), SW_HTTP2_DEFAULT_MAX_FRAME_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + + http2::Settings _settings = { + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + }; + + http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, _settings.header_table_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, _settings.enable_push); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, _settings.max_concurrent_streams); + http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, _settings.init_window_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, _settings.max_frame_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, _settings.max_header_list_size); + + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), _settings.header_table_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), _settings.enable_push); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), _settings.max_concurrent_streams); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), _settings.init_window_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), _settings.max_frame_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), _settings.max_header_list_size); +} diff --git a/core-tests/src/protocol/mime_type.cpp b/core-tests/src/protocol/mime_type.cpp index df8a396a844..eabd700f828 100644 --- a/core-tests/src/protocol/mime_type.cpp +++ b/core-tests/src/protocol/mime_type.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/redis.cpp b/core-tests/src/protocol/redis.cpp index c89b3095bf6..0ab9f144480 100644 --- a/core-tests/src/protocol/redis.cpp +++ b/core-tests/src/protocol/redis.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -50,8 +50,8 @@ TEST(redis, server) { serv.create(); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { - if (worker_id != 0) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id != 0) { return; } swoole::Coroutine::create( @@ -66,7 +66,7 @@ TEST(redis, server) { serv); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { int session_id = req->info.fd; auto list = redis::parse(req->data, req->info.len); diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index b4e567021a5..aa018aee9d7 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -13,13 +13,15 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ #include "test_core.h" #include "swoole_pipe.h" +#include "swoole_util.h" +using namespace std; using namespace swoole; TEST(reactor, create) { @@ -92,6 +94,7 @@ TEST(reactor, wait) { }); ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(swoole_event_get_socket(p.get_socket(false)->get_fd()), p.get_socket(false)); ASSERT_EQ(ret, SW_OK); ret = p.write((void *) SW_STRS("hello world")); @@ -192,7 +195,7 @@ TEST(reactor, bad_fd) { ASSERT_EQ(n, SW_ERR); ASSERT_EQ(swoole_get_last_error(), EBADF); swoole_event_free(); - sock->fd = -1; + sock->move_fd(); sock->free(); } @@ -269,3 +272,104 @@ TEST(reactor, defer_task) { ASSERT_EQ(count, 1); swoole_event_free(); } + +static void event_end_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, cycle) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.future_task.callback = event_end_callback; + reactor.future_task.data = (void *) test; + reactor_test_func(&reactor); +} + +static void event_idle_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, priority_idle_task) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.idle_task.callback = event_idle_callback; + reactor.idle_task.data = (void *) test; + reactor_test_func(&reactor); +} + +TEST(reactor, hook) { + Reactor *reactor = new Reactor(1024, Reactor::TYPE_SELECT); + reactor->wait_exit = true; + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_CREATE, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + ON_SCOPE_EXIT { + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE] = nullptr; + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_DESTROY] = nullptr; + }; + + reactor_test_func(reactor); + delete reactor; +} + +TEST(reactor, set_fd) { + UnixSocket p(true, SOCK_DGRAM); + Reactor *reactor = new Reactor(1024, Reactor::TYPE_EPOLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_POLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_SELECT); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; +} + +static void error_event(Reactor::Type type) { + Pipe p(true); + ASSERT_TRUE(p.ready()); + + Reactor *reactor = new Reactor(1024, type); + + reactor->set_handler(SW_FD_PIPE | SW_EVENT_ERROR, [](Reactor *reactor, Event *event) -> int { + EXPECT_EQ(reactor->del(event->socket), SW_OK); + reactor->running = false; + return SW_OK; + }); + + reactor->add(p.get_socket(true), SW_EVENT_ERROR); + reactor->add(p.get_socket(false), SW_EVENT_ERROR); + + p.close(SW_PIPE_CLOSE_WORKER); + reactor->wait(nullptr); + delete reactor; +} +TEST(reactor, error_ev) { + error_event(Reactor::TYPE_EPOLL); + error_event(Reactor::TYPE_POLL); +} diff --git a/core-tests/src/server/buffer.cpp b/core-tests/src/server/buffer.cpp index 3b077fe8bd0..9c0df8f346f 100644 --- a/core-tests/src/server/buffer.cpp +++ b/core-tests/src/server/buffer.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,12 +26,12 @@ using namespace swoole; static const char *packet = "hello world\n"; TEST(server, send_buffer) { - swServer serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -65,9 +65,9 @@ TEST(server, send_buffer) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); swString resp(1024 * 1024 * 16); diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index 4122f4a54a6..1fc7058478e 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,9 +24,17 @@ #include "swoole_server.h" #include "swoole_file.h" #include "swoole_http.h" +#include "swoole_util.h" using namespace swoole; using namespace std; +using http_server::Context; +using network::Client; +using network::SyncClient; + +SessionId session_id = 0; +Connection *conn = nullptr; +Session *session = nullptr; struct http_context { unordered_map headers; @@ -48,7 +56,7 @@ struct http_context { } void response(int code) { - swString *buf = swoole::make_string(1024); + String *buf = make_string(1024); buf->length = sw_snprintf(buf->str, buf->size, "HTTP/1.1 %s\r\n", http_server::get_status_message(code)); for (auto &kv : response_headers) { buf->append(kv.first.c_str(), kv.first.length()); @@ -86,10 +94,12 @@ static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { return 0; } -static void test_run_server(function fn) { +static void test_base_server(function fn) { thread child_thread; - swServer serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; + serv.enable_reuse_port = true; + serv.heartbeat_check_interval = 1; serv.private_data_2 = (void *) &fn; serv.enable_static_handler = true; @@ -98,7 +108,7 @@ static void test_run_server(function fn) { sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -108,20 +118,33 @@ static void test_run_server(function fn) { serv.create(); - serv.onWorkerStart = [&child_thread](swServer *serv, int worker_id) { - function fn = *(function *) serv->private_data_2; + serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) { + function fn = *(function *) serv->private_data_2; child_thread = thread(fn, serv); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { - SessionId session_id = req->info.fd; - auto conn = serv->get_connection_by_session_id(session_id); + serv.onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + EXPECT_EQ(info->reactor_id, -1); + } else { + EXPECT_GE(info->reactor_id, 0); + } + } + }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); - if (conn->websocket_status == swoole::websocket::STATUS_ACTIVE) { + if (conn->websocket_status == websocket::STATUS_ACTIVE) { sw_tg_buffer()->clear(); std::string resp = "Swoole: " + string(req->data, req->info.len); - swoole::websocket::encode( - sw_tg_buffer(), resp.c_str(), resp.length(), swoole::websocket::OPCODE_TEXT, swoole::websocket::FLAG_FIN); + websocket::encode(sw_tg_buffer(), + resp.c_str(), + resp.length(), + websocket::OPCODE_TEXT, + websocket::FLAG_FIN); serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length); return SW_OK; } @@ -151,7 +174,7 @@ static void test_run_server(function fn) { ctx.response(SW_HTTP_SWITCHING_PROTOCOLS); - conn->websocket_status = swoole::websocket::STATUS_ACTIVE; + conn->websocket_status = websocket::STATUS_ACTIVE; return SW_OK; } @@ -162,7 +185,12 @@ static void test_run_server(function fn) { } EXPECT_EQ(err, HPE_OK); - ctx.response(SW_HTTP_OK, "hello world"); + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + serv->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); @@ -173,8 +201,153 @@ static void test_run_server(function fn) { child_thread.join(); } +static Server *test_process_server(Server::DispatchMode dispatch_mode = Server::DISPATCH_FDMOD, bool is_ssl = false) { + Server *server = new Server(Server::MODE_PROCESS); + server->user_ = std::string("root"); + server->group_ = std::string("root"); + server->chroot_ = std::string("/"); + server->worker_num = 2; + server->dispatch_mode = dispatch_mode; + server->open_cpu_affinity = true; + sw_logger()->set_level(SW_LOG_WARNING); + + conn = nullptr; + session = nullptr; + ListenPort *port = is_ssl ? server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0) + : server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + port->open_tcp_keepalive = 1; + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->create(); + + server->onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + ASSERT_EQ(info->reactor_id, -1); + } else { + ASSERT_GE(info->reactor_id, 0); + } + } + }; + + server->onReceive = [&](Server *serv, RecvData *req) -> int { + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); + session = serv->get_session(session_id); + + EXPECT_LE(serv->get_idle_worker_num(), serv->worker_num); + EXPECT_TRUE(serv->is_healthy_connection(microtime(), conn)); + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = serv; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/overflow") { + conn->overflow = 1; + } + + if (ctx.url == "/pause") { + serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV); + } + + EXPECT_EQ(err, HPE_OK); + ctx.response(SW_HTTP_OK, "hello world"); + + return SW_OK; + }; + + return server; +} + +static Server *test_proxy_server() { + Server *server = new Server(Server::MODE_BASE); + server->worker_num = 1; + + ListenPort *port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + port->kernel_socket_send_buffer_size = INT_MAX; + port->kernel_socket_recv_buffer_size = INT_MAX; + port->open_tcp_nodelay = true; + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->get_primary_port()->set_package_max_length(64 * 1024); + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + + server->create(); + + server->onReceive = [&](Server *server, RecvData *req) -> int { + session_id = req->info.fd; + conn = server->get_connection_by_session_id(session_id); + + SwooleG.process_id = server->worker_num; + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = server; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + server->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } + + EXPECT_EQ(err, HPE_OK); + EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); + return SW_OK; + }; + + return server; +} + TEST(http_server, get) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -188,8 +361,75 @@ TEST(http_server, get) { }); } +TEST(http_server, heartbeat_check_interval) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + sleep(3); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, not_active) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + + conn->active = 0; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, has_closed) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + + conn->closed = 1; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, idle_time) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + port->max_idle_time = 1; + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + + sleep(2); + kill(getpid(), SIGTERM); + }); +} + TEST(http_server, post) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -207,7 +447,7 @@ TEST(http_server, post) { } TEST(http_server, static_get) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -224,10 +464,131 @@ TEST(http_server, static_get) { EXPECT_EQ(resp->body, str->to_std_string()); + resp = cli.Get("/just/get/file"); + EXPECT_EQ(resp, nullptr); kill(getpid(), SIGTERM); }); } +TEST(http_server, static_files) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + std::string::size_type postion = resp->body.find("Index of"); + EXPECT_TRUE(postion != std::string::npos); + + // directory not exists + resp = cli.Get("/test/../"); + EXPECT_EQ(resp->status, 404); + + // must be document_root + resp = cli.Get("//tests/../"); + EXPECT_EQ(resp->status, 200); + + resp = cli.Get("/tests/../README.md"); + EXPECT_EQ(resp->status, 200); + + // file not exists + resp = cli.Get("/not-exists.jpg"); + EXPECT_EQ(resp->status, 404); + + // try again + serv->add_static_handler_index_files("README.md"); + resp = cli.Get("/"); + postion = resp->body.find("

"); + EXPECT_TRUE(postion != std::string::npos); + + kill(getpid(), SIGTERM); + }); +} + +static void request_with_header(const char *date_format, httplib::Client *cli) { + char temp[128] = {0}; + time_t raw_time = time(NULL) + 7 * 24 * 60 * 60; + tm *time_info = gmtime(&raw_time); + + strftime(temp, sizeof(temp), date_format, time_info); + httplib::Headers headers = {{"If-Modified-Since", temp}}; + auto resp = cli->Get("/", headers); + EXPECT_EQ(resp, nullptr); +} + +TEST(http_server, not_modify) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + serv->add_static_handler_index_files("swoole-logo.svg"); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + + // 304 not modified + cli.set_read_timeout(0, 100); + request_with_header(SW_HTTP_RFC1123_DATE_GMT, &cli); + request_with_header(SW_HTTP_RFC1123_DATE_UTC, &cli); + request_with_header(SW_HTTP_RFC850_DATE, &cli); + request_with_header(SW_HTTP_ASCTIME_DATE, &cli); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, proxy_file) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/just/get/file"); + ASSERT_EQ(resp, nullptr); + } +} + +// need fix +TEST(http_server, proxy_response) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + // ASSERT_EQ(resp->body, string("hello world")); + } +} + static void websocket_test(int server_port, const char *data, size_t length) { httplib::Client cli(TEST_HOST, server_port); @@ -240,7 +601,7 @@ static void websocket_test(int server_port, const char *data, size_t length) { } TEST(http_server, websocket_small) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); websocket_test(serv->get_primary_port()->get_port(), SW_STRL("hello world, swoole is best!")); kill(getpid(), SIGTERM); @@ -248,10 +609,10 @@ TEST(http_server, websocket_small) { } TEST(http_server, websocket_medium) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); - swString str(8192); + String str(8192); str.repeat("A", 1, 8192); websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); @@ -260,13 +621,622 @@ TEST(http_server, websocket_medium) { } TEST(http_server, websocket_big) { - test_run_server([](swServer *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); - swString str(128 * 1024); + String str(128 * 1024); str.repeat("A", 1, str.capacity() - 1); websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); kill(getpid(), SIGTERM); }); } + +TEST(http_server, parser1) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req1.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, parser2) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->get_primary_port()->set_package_max_length(64 * 1024); + server->upload_max_filesize = 1024 * 1024; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req2.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, heartbeat) { + Server *server = test_process_server(); + server->heartbeat_check_interval = 0; + auto port = server->get_primary_port(); + port->set_package_max_length(1024); + port->heartbeat_idle_time = 2; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + sleep(10); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, overflow) { + Server *server = test_process_server(); + auto port = server->get_primary_port(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + resp = cli.Get("/overflow"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, process) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + } +} + +TEST(http_server, process1) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + sleep(1); + resp = cli.Get("/examples/test.jpg"); + ASSERT_EQ(resp->status, 200); + } +} + +TEST(http_server, redundant_callback) { + Server *server = test_process_server(Server::DISPATCH_IDLE_WORKER); + server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onClose = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferEmpty = [](Server *serv, DataHead *info) -> int { return 0; }; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + ASSERT_EQ(server->onConnect, nullptr); + ASSERT_EQ(server->onClose, nullptr); + ASSERT_EQ(server->onBufferFull, nullptr); + ASSERT_EQ(server->onBufferEmpty, nullptr); + exit(0); + } + + if (pid > 0) { + sleep(2); + kill(server->get_master_pid(), SIGTERM); + } +} + +TEST(http_server, pause) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/pause"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, sni) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + SSLContext *context = new SSLContext(); + *context = *port->ssl_context; + context->cert_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_cert.pem"; + context->key_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_key.pem"; + port->ssl_add_sni_cert("localhost", context); + port->ssl_context->protocols = 0; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string command = "curl https://localhost:" + port_num + " -k -vvv --stderr /tmp/wwwsnitestcom.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream wwwsnitestcom; + wwwsnitestcom.open("/tmp/wwwsnitestcom.txt"); + ASSERT_TRUE(wwwsnitestcom.is_open()); + buffer << wwwsnitestcom.rdbuf(); + wwwsnitestcom.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("CN=cs.php.net") != string::npos); + + string command2 = "curl https://127.0.0.1:" + port_num + " -k -vvv --stderr /tmp/wwwsnitest2com.txt"; + swoole_shell_exec(command2.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer2; + ifstream wwwsnitest2com; + wwwsnitest2com.open("/tmp/wwwsnitest2com.txt"); + ASSERT_TRUE(wwwsnitest2com.is_open()); + buffer2 << wwwsnitest2com.rdbuf(); + string response2(buffer2.str()); + wwwsnitest2com.close(); + ASSERT_TRUE(response2.find("CN=127.0.0.1") != string::npos); + } +} + +TEST(http_server, bad_request) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string str_1 = "curl -X UNKNOWN http://"; + string str_2 = ":"; + string str_3 = " -k -vvv --stderr /tmp/bad_request.txt"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port + str_3; + + pid_t pid2; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream bad_request; + bad_request.open("/tmp/bad_request.txt"); + ASSERT_TRUE(bad_request.is_open()); + buffer << bad_request.rdbuf(); + string response(buffer.str()); + bad_request.close(); + ASSERT_TRUE(response.find("400 Bad Request") != string::npos); + } +} + +TEST(http_server, chunked) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, max_queued_bytes) { + Server *server = test_process_server(); + server->max_queued_bytes = 100; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, dispatch_func_return_error_worker_id) { + Server *server = test_process_server(); + server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int { + return data->info.fd % 2 == 0 ? Server::DISPATCH_RESULT_DISCARD_PACKET + : Server::DISPATCH_RESULT_CLOSE_CONNECTION; + }; + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + }; + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_read_timeout(1, 0); + auto resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, client_ca) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-cert.pem"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-key.pem"); + port->ssl_context->verify_peer = true; + port->ssl_context->allow_self_signed = true; + port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string client_cert = " --cert " + test::get_root_path() + "/tests/include/api/ssl-ca/client-cert.pem "; + string client_key = "--key " + test::get_root_path() + "/tests/include/api/ssl-ca/client-key.pem"; + string command = "curl https://127.0.0.1:" + port_num + " " + client_cert + client_key + + " -k -vvv --stderr /tmp/client_ca.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream client_ca; + client_ca.open("/tmp/client_ca.txt"); + ASSERT_TRUE(client_ca.is_open()); + buffer << client_ca.rdbuf(); + client_ca.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("200 OK") != response.npos); + } +} + +static bool request_with_if_range_header(const char *date_format, std::string port) { + struct stat file_stat; + std::string file_path = test::get_root_path() + "/docs/swoole-logo.svg"; + stat(file_path.c_str(), &file_stat); + time_t file_mtime = file_stat.st_mtim.tv_sec; + struct tm *time_info = gmtime(&file_mtime); + + char temp[128] = {0}; + strftime(temp, sizeof(temp), date_format, time_info); + + string str_1 = "curl http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=0-500' -H 'If-Range: "; + string command = str_1 + host + str_2 + port + str_3 + headers + string(temp) + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + sleep(2); + + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos && response.find("Content-Length: 501") != string::npos; +} + +TEST(http_server, http_range) { + Server *server = test_process_server(); + server->http_autoindex = true; + server->add_static_handler_location("/docs"); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port = to_string(server->get_primary_port()->port); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_GMT, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_UTC, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC850_DATE, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_ASCTIME_DATE, port)); + } +} + +static bool request_with_diff_range(std::string port, std::string range) { + string str_1 = "curl -X GET http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=" + range; + string command = str_1 + host + str_2 + port + str_3 + headers + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + + sleep(2); + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos; +} + +TEST(http_server, http_range2) { + Server *server = test_process_server(); + server->add_static_handler_location("/docs"); + server->add_static_handler_index_files("swoole-logo.svg"); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + exit(0); + } + + if (pid == 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-15")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "16-31")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "-16")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "128-")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-0,-1")); + + } +} + +// it is always last test +TEST(http_server, abort_connection) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.max_sockets = 2; + serv.set_max_connection(1); + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + port->open_http_protocol = 1; + serv.create(); + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + + if (worker->id == 0) { + sleep(1); + kill(serv->get_master_pid(), SIGTERM); + } + }; + + serv.onReceive = [&](Server *server, RecvData *req) -> int { return SW_OK; }; + serv.start(); +} diff --git a/core-tests/src/server/http_parser.cpp b/core-tests/src/server/http_parser.cpp new file mode 100644 index 00000000000..f7e3b6e0d95 --- /dev/null +++ b/core-tests/src/server/http_parser.cpp @@ -0,0 +1,575 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "swoole_http_parser.h" + +using namespace std; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; +// clang-format on + +typedef struct { + long fd; + uchar completed : 1; + uchar end_ : 1; + uchar send_header_ : 1; + + uchar send_chunked : 1; + uchar recv_chunked : 1; + uchar send_trailer_ : 1; + uchar keepalive : 1; + uchar websocket : 1; + + uchar upgrade : 1; + uchar detached : 1; + uchar parse_cookie : 1; + uchar parse_body : 1; + uchar parse_files : 1; + uchar co_socket : 1; + uchar http2 : 1; + + swoole_http_parser parser; + + uint16_t input_var_num; + char *current_header_name; + size_t current_header_name_len; + char *current_input_name; + size_t current_input_name_len; + char *current_form_data_name; + size_t current_form_data_name_len; + + vector header_fields; + vector header_values; + string query_string; +} HttpContext; + +static swoole_http_parser *swoole_http_parser_create(swoole_http_parser_type type = PHP_HTTP_REQUEST) { + HttpContext *ctx = new HttpContext(); + swoole_http_parser *parser = &ctx->parser; + swoole_http_parser_init(parser, type); + parser->data = ctx; + return parser; +} + +static void swoole_http_destroy_context(swoole_http_parser *parser) { + delete (HttpContext *) parser->data; + return; +} + +static int swoole_http_parser_method(string protocol) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, protocol.c_str(), protocol.length()); + + int ret = parser->method; + swoole_http_destroy_context(parser); + return ret; +} + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->query_string = string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_fields.push_back(string(at, length)); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_values.push_back(string(at, length)); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_message_complete(swoole_http_parser *parser) { + return 0; +} + +static const string request_get = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http2 = "GET /get HTTP/2\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http09 = "GET /index.html\r\n"; + +static const string request_head = "HEAD /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string = "GET /get?a=foo&b=bar&c=456%26789#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string2 = "GET /get? HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string3 = "GET /index.html?a=123\r"; + +static const string request_get_with_query_string4 = "GET /index.html?a=123\n"; + +static const string request_get_with_query_string5 = "GET /get#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_schema = "GET http://127.0.0.1:8081/get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_proxy_connection = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_connection_close = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: close\r\n" + "\r\n"; + +static const string request_get_http10 = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "\r\n"; + +static const string request_get_http10_with_keep_alive = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_post = "POST /api/build/v1/foo HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: 7\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string request_upgrade = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "upgrade: websocket\r\n" + "User-Agent: curl/7.64.1\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + +static const string request_dead = "POST /dead HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: abcd\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string response_200 = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_200_without_ok = "HTTP/1.1 200\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_chunk = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "Transfer-Encoding: chunked\r\n" + "Connection: close\r\n" + "\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "0\r\n" + "\r\n"; + +TEST(http_parser, method_name) { + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_DELETE), "DELETE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_GET), "GET"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_HEAD), "HEAD"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_POST), "POST"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PUT), "PUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PATCH), "PATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CONNECT), "CONNECT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_OPTIONS), "OPTIONS"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_TRACE), "TRACE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_COPY), "COPY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_LOCK), "LOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCOL), "MKCOL"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MOVE), "MOVE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCALENDAR), "MKCALENDAR"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPFIND), "PROPFIND"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPPATCH), "PROPPATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SEARCH), "SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNLOCK), "UNLOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_REPORT), "REPORT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKACTIVITY), "MKACTIVITY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CHECKOUT), "CHECKOUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MERGE), "MERGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MSEARCH), "M-SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOTIFY), "NOTIFY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SUBSCRIBE), "SUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNSUBSCRIBE), "UNSUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PURGE), "PURGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOT_IMPLEMENTED), "NOTIMPLEMENTED"); +} + +TEST(http_parser, http_version) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http2.c_str(), request_get_http2.length()); + ASSERT_TRUE(parser->http_major == 2); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http09.c_str(), request_get_http09.length()); + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, should_keep_alive) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_connection_close.c_str(), + request_get_with_connection_close.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_http10_with_keep_alive.c_str(), + request_get_http10_with_keep_alive.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_schema.c_str(), request_get_with_schema.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_head.c_str(), request_head.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, upgrade) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_upgrade.c_str(), request_upgrade.length()); + ASSERT_TRUE(parser->upgrade == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, dead) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_dead.c_str(), request_dead.length()); + ASSERT_TRUE(parser->state == s_dead); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, zero) { + swoole_http_parser *parser = swoole_http_parser_create(); + int ret = swoole_http_parser_execute(parser, &http_parser_settings, "", 0); + ASSERT_TRUE(ret == 0); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, methods) { + ASSERT_EQ(swoole_http_parser_method("COPY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_COPY); + ASSERT_EQ(swoole_http_parser_method("CHECKOUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_CHECKOUT); + ASSERT_EQ(swoole_http_parser_method("HEAD /get HTTP/1.1\r\n\r\n"), PHP_HTTP_HEAD); + ASSERT_EQ(swoole_http_parser_method("LOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_LOCK); + ASSERT_EQ(swoole_http_parser_method("MOVE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MOVE); + ASSERT_EQ(swoole_http_parser_method("MKCALENDAR /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKCALENDAR); + ASSERT_EQ(swoole_http_parser_method("MKACTIVITY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKACTIVITY); + ASSERT_EQ(swoole_http_parser_method("MERGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MERGE); + ASSERT_EQ(swoole_http_parser_method("M-SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MSEARCH); + ASSERT_EQ(swoole_http_parser_method("NOTIFY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOTIFY); + ASSERT_EQ(swoole_http_parser_method("OPTIONS /get HTTP/1.1\r\n\r\n"), PHP_HTTP_OPTIONS); + ASSERT_EQ(swoole_http_parser_method("REPORT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_REPORT); + ASSERT_EQ(swoole_http_parser_method("SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SEARCH); + ASSERT_EQ(swoole_http_parser_method("SUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("UNSUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNSUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("TRACE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_TRACE); + ASSERT_EQ(swoole_http_parser_method("UNLOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNLOCK); + ASSERT_EQ(swoole_http_parser_method("PURGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PURGE); + ASSERT_EQ(swoole_http_parser_method("POST /get HTTP/1.1\r\n\r\n"), PHP_HTTP_POST); + ASSERT_EQ(swoole_http_parser_method("PROPFIND /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPFIND); + ASSERT_EQ(swoole_http_parser_method("PROPPATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPPATCH); + ASSERT_EQ(swoole_http_parser_method("PUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PUT); + ASSERT_EQ(swoole_http_parser_method("PATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PATCH); + ASSERT_EQ(swoole_http_parser_method("UNKNOWN /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOT_IMPLEMENTED); +} + +TEST(http_parser, proxy_connection) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_proxy_connection.c_str(), + request_get_with_proxy_connection.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[4].c_str(), "Proxy-Connection"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, header_field_and_value) { + string header = "User-Agent: curl/7.64.1\r\n" + "\r\n"; + + swoole_http_parser *parser = swoole_http_parser_create(); + parser->state = s_header_field; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[0].c_str(), "User-Agent"); + swoole_http_destroy_context(parser); + + header = "curl/7.64.1\r\n" + "\r\n"; + parser = swoole_http_parser_create(); + parser->state = s_header_value; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + ASSERT_STREQ(ctx->header_values[0].c_str(), "curl/7.64.1"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, response) { + swoole_http_parser *parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute( + parser, &http_parser_settings, response_200_without_ok.c_str(), response_200_without_ok.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_chunk.c_str(), response_chunk.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, query_string) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string.c_str(), request_get_with_query_string.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=foo&b=bar&c=456%26789"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string2.c_str(), request_get_with_query_string2.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string3.c_str(), request_get_with_query_string3.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string4.c_str(), request_get_with_query_string4.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string5.c_str(), request_get_with_query_string5.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, http09) { + string request_get_with_query_string_http09 = "GET /index.html\r"; + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + request_get_with_query_string_http09 = "GET /index.html\n"; + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); +} diff --git a/core-tests/src/server/message_bus.cpp b/core-tests/src/server/message_bus.cpp index c98568624ea..52f96e78a4f 100644 --- a/core-tests/src/server/message_bus.cpp +++ b/core-tests/src/server/message_bus.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/server/multipart_parser.cpp b/core-tests/src/server/multipart_parser.cpp new file mode 100644 index 00000000000..aac4e30ef2f --- /dev/null +++ b/core-tests/src/server/multipart_parser.cpp @@ -0,0 +1,255 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "multipart_parser.h" + +struct MppResult { + std::string data; + std::string header_field; + std::string header_value; + bool header_complete; + bool body_end; +}; + +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_field: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_field = std::string(at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_value: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_value = std::string(at, length); + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_data: length=%lu", length); + auto res = (MppResult *) p->data; + res->data.append(at, length); + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete"); + auto res = (MppResult *) p->data; + res->header_complete = true; + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end"); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begin"); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + swoole_trace("on_body_end"); + auto res = (MppResult *) p->data; + res->body_end = true; + return 0; +} + +static multipart_parser_settings _settings{ + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + +static const std::string boundary = "--WebKitFormBoundaryeGOz80A8JnaO6kuw"; + +static multipart_parser *create_parser() { + return multipart_parser_init(boundary.c_str(), boundary.length(), &_settings); +} + +static void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) { + size_t length = 1024; + char buf[length]; + + parser->error_reason = error_reason; + int result_len = multipart_parser_error_msg(parser, buf, length); + ASSERT_GT(result_len, 0); + buf[result_len] = '\0'; + + std::string response(buf, result_len); + ASSERT_TRUE(response.find(error) != response.npos); +} + +TEST(multipart_parser, error_message) { + size_t length = 1024; + char buf[length]; + auto parser = create_parser(); + + parser->error_reason = MPPE_OK; + ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0); + + parser->error_expected = '\0'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\r'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\n'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = 'a'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); +} + +TEST(multipart_parser, header_field) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + ASSERT_STREQ(result.header_field.c_str(), "Content-Disposition"); + ASSERT_TRUE(result.header_value.find("test") != result.header_value.npos); + + std::string boundary_str(parser->boundary, parser->boundary_length); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); +} + +TEST(multipart_parser, header_error) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\""); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, -1); + ASSERT_EQ(parser->error_reason, MPPE_HEADER_VALUE_INCOMPLETE); + ASSERT_EQ(parser->error_expected, '\r'); +} + +TEST(multipart_parser, data) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + std::string boundary_str(parser->boundary, parser->boundary_length); + + // data part + swoole::String data(128); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + data.append_random_bytes(swoole_rand(60, 120), true); + ASSERT_EQ(multipart_parser_execute(parser, data.value(), data.get_length()), data.get_length()); + + auto append_data = [&]() { + size_t offset = data.length; + data.append_random_bytes(swoole_rand(60, 120), true); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + }; + + append_data(); + data.append("\r"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r")), 1); + + append_data(); + + data.append("\r\n"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + + append_data(); + + { + size_t offset = data.length; + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + } + + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); + + ASSERT_MEMEQ(data.value(), result.data.c_str(), result.data.length()); + + ASSERT_TRUE(result.header_complete); + ASSERT_TRUE(result.body_end); +} diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 97f6b4890cb..7d75e54d4aa 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -23,10 +23,13 @@ #include "swoole_memory.h" #include "swoole_signal.h" #include "swoole_lock.h" +#include "swoole_util.h" using namespace std; using namespace swoole; +int beforeReloadPid = 0; + TEST(server, schedule) { int ret; Server serv(Server::MODE_PROCESS); @@ -36,7 +39,7 @@ TEST(server, schedule) { ASSERT_EQ(SW_OK, ret); for (uint32_t i = 0; i < serv.worker_num; i++) { - serv.workers[i].status = SW_WORKER_BUSY; + serv.workers[i].set_status_to_busy(); } std::set _worker_id_set; @@ -48,7 +51,7 @@ TEST(server, schedule) { ASSERT_EQ(_worker_id_set.size(), serv.worker_num); for (uint32_t i = 1; i < serv.worker_num - 1; i++) { - serv.workers[i].status = SW_WORKER_IDLE; + serv.workers[i].set_status_to_idle(); } _worker_id_set.clear(); @@ -67,7 +70,7 @@ TEST(server, base) { sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); ASSERT_TRUE(port); mutex lock; @@ -80,7 +83,7 @@ TEST(server, base) { lock.lock(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.send(packet, strlen(packet)); char buf[1024]; @@ -90,9 +93,9 @@ TEST(server, base) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -124,15 +127,15 @@ TEST(server, process) { ASSERT_EQ(serv.create(), SW_OK); - serv.onStart = [&lock](swServer *serv) { + serv.onStart = [&lock](Server *serv) { thread t1([=]() { swoole_signal_block_all(); lock->lock(); - swListenPort *port = serv->get_primary_port(); + ListenPort *port = serv->get_primary_port(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.send(packet, strlen(packet)); char buf[1024]; @@ -144,9 +147,9 @@ TEST(server, process) { t1.detach(); }; - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -160,6 +163,205 @@ TEST(server, process) { delete lock; } +#ifdef SW_THREAD +TEST(server, thread) { + Server serv(Server::MODE_THREAD); + serv.worker_num = 2; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + serv.shutdown(); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} +#endif + +TEST(server, reload_all_workers) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + serv.task_enable_coroutine = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + std::string filename = "/tmp/worker_1.pid"; + if (worker->id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->manager_pid, SIGUSR2); + sleep(1); + kill(serv->gs->manager_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, reload_all_workers2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + std::string filename = "/tmp/worker_2.pid"; + if (worker->id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->master_pid, SIGUSR2); + sleep(1); + kill(serv->gs->master_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](Server *serv, EventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + sleep(1); + kill(serv->get_manager_pid(), SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers1) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + #ifdef SW_USE_OPENSSL TEST(server, ssl) { Server serv(Server::MODE_PROCESS); @@ -195,7 +397,7 @@ TEST(server, ssl) { EXPECT_EQ(port->ssl, 1); EXPECT_EQ(swoole_ssl_is_thread_safety(), true); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.enable_ssl_encrypt(); c.send(packet, strlen(packet)); @@ -208,7 +410,7 @@ TEST(server, ssl) { t1.detach(); }; - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -257,7 +459,7 @@ TEST(server, dtls) { EXPECT_EQ(port->ssl, 1); - swoole::network::SyncClient c(SW_SOCK_UDP); + network::SyncClient c(SW_SOCK_UDP); c.connect(TEST_HOST, port->port); c.enable_ssl_encrypt(); c.send(packet, strlen(packet)); @@ -270,7 +472,7 @@ TEST(server, dtls) { t1.detach(); }; - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -285,6 +487,51 @@ TEST(server, dtls) { delete lock; } + +TEST(server, dtls2) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->single_thread = false; + ListenPort *port = server->add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + port->ssl_init(); + + server->create(); + server->onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + + sleep(1); + auto port = server->get_primary_port(); + + network::SyncClient c(SW_SOCK_UDP); + c.connect(TEST_HOST, port->port); + c.enable_ssl_encrypt(); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + } +} #endif TEST(server, task_worker) { @@ -298,9 +545,12 @@ TEST(server, task_worker) { exit(2); } - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, EventData *task) -> int { + EXPECT_EQ(serv->get_tasking_num(), 1); EXPECT_EQ(string(task->data, task->info.len), string(packet)); serv->gs->task_workers.running = 0; + serv->gs->task_count++; + serv->gs->tasking_num--; return 0; }; @@ -310,6 +560,10 @@ TEST(server, task_worker) { thread t1([&serv]() { serv.gs->task_workers.running = 1; serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); + EXPECT_EQ(serv.get_tasking_num(), 0); + serv.gs->tasking_num--; + EXPECT_EQ(serv.get_tasking_num(), 0); + EXPECT_EQ(serv.get_idle_task_worker_num(), serv.task_worker_num); }); usleep(10000); @@ -323,10 +577,245 @@ TEST(server, task_worker) { int _dst_worker_id = 0; - ASSERT_GE(serv.gs->task_workers.dispatch(&buf, &_dst_worker_id), 0); + ASSERT_TRUE(serv.task(&buf, &_dst_worker_id)); + ASSERT_EQ(serv.gs->task_count, 1); t1.join(); serv.gs->task_workers.destroy(); + + ASSERT_EQ(serv.gs->task_count, 2); +} + +// PHP_METHOD(swoole_server, task) +TEST(server, task_worker2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker->id; + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK); + ASSERT_EQ(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), SW_OK); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskCo) +TEST(server, task_worker3) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskwait) +TEST(server, task_worker4) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + + EventData *task_result = serv->get_task_result(); + sw_memset_zero(task_result, sizeof(*task_result)); + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker->id; + sw_atomic_fetch_add(&serv->gs->tasking_num, 1); + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sw_atomic_fetch_add(&serv->gs->tasking_num, 0); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskWaitMulti) +TEST(server, task_worker5) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + char data[SW_IPC_MAX_SIZE * 2] = {}; + swoole_random_string(data, SW_IPC_MAX_SIZE * 2); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [&data](Server *serv, swEventData *task) -> int { + PacketTask *pkg = (PacketTask *) task->data; + ifstream ifs; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + + EXPECT_EQ(string(resp), string(data)); + EXPECT_TRUE(serv->finish(resp, SW_IPC_MAX_SIZE * 2, 0, task)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&data](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData *task_result = &(serv->task_results[worker->id]); + sw_memset_zero(task_result, sizeof(*task_result)); + + File fp = make_tmpfile(); + std::string file_path = fp.get_path(); + fp.close(); + int *finish_count = (int *) task_result->data; + *finish_count = 0; + + swoole_strlcpy(task_result->data + 4, file_path.c_str(), SW_TASK_TMP_PATH_SIZE); + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + Server::task_pack(&buf, data, SW_IPC_MAX_SIZE * 2); + buf.info.ext_flags |= SW_TASK_WAITALL; + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(3); + + ifstream ifs; + ifs.open(task_result->data + 4); + char recv[sizeof(EventData)] = {0}; + ifs >> recv; + ifs.close(); + + EventData *task = (EventData *) recv; + PacketTask *pkg = (PacketTask *) task->data; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + EXPECT_EQ(string(resp), string(data)); + + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); } TEST(server, max_connection) { @@ -453,7 +942,7 @@ void test_command(enum Server::Mode _mode) { serv->command(1, Server::Command::MASTER, "test", "hello world [0]", fn); }; - serv.onWorkerStart = [](Server *serv, int worker_id) { + serv.onWorkerStart = [](Server *serv, Worker *worker) { }; @@ -478,3 +967,263 @@ TEST(server, command_1) { TEST(server, command_2) { test_command(Server::MODE_BASE); } + +TEST(server, sendwait) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, system) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + mutex lock; + lock.lock(); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + int svr_port = swoole::test::get_random_port(); + struct sockaddr_in serv_addr; + bzero(&serv_addr, sizeof(serv_addr)); + serv_addr.sin_addr.s_addr = inet_addr(TEST_HOST); + serv_addr.sin_port = htons(svr_port); + serv_addr.sin_family = AF_INET; + bind(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr)); + listen(fd, 1024); + + setenv("LISTEN_FDS_START", to_string(fd).c_str(), 1); + setenv("LISTEN_FDS", "1", 1); + setenv("LISTEN_PID", to_string(getpid()).c_str(), 1); + + EXPECT_GT(serv.add_systemd_socket(), 0); + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, svr_port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, reopen_log) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + string filename = "/tmp/swoole.log"; + sw_logger()->open(filename.c_str()); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&filename](Server *serv, Worker *worker) { + if (worker->id != 0) { + return; + } + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + unlink(filename.c_str()); + EXPECT_TRUE(access(filename.c_str(), R_OK) == -1); + kill(serv->gs->master_pid, SIGRTMIN); + sleep(2); + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + kill(serv->gs->master_pid, SIGTERM); + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, udp_packet) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_UDP, TEST_HOST, 0); + + server->create(); + server->onPacket = [](Server *serv, RecvData *req) { + DgramPacket *recv_data = (DgramPacket *) req->data; + EXPECT_EQ(string(recv_data->data, recv_data->length), string(packet)); + network::Socket *server_socket = serv->get_server_socket(req->info.server_fd); + string resp = string(packet); + server_socket->sendto(recv_data->socket_addr, resp.c_str(), resp.length(), 0); + return SW_OK; + }; + + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + auto port = server->get_primary_port(); + + network::Client cli(SW_SOCK_UDP, false); + int ret = cli.connect(&cli, TEST_HOST, port->port, -1, 0); + EXPECT_EQ(ret, 0); + ret = cli.send(&cli, packet, strlen(packet), 0); + EXPECT_GT(ret, 0); + + char buf[1024]; + sleep(1); + cli.recv(&cli, buf, 128, 0); + ASSERT_STREQ(buf, packet); + cli.close(); + } +} + +TEST(server, protocols) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + + port->open_eof_check = true; + ASSERT_STREQ(port->get_protocols(), "eof"); + port->open_eof_check = false; + + port->open_length_check = true; + ASSERT_STREQ(port->get_protocols(), "length"); + port->open_length_check = false; + + port->open_http_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http"); + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2|websocket"); + port->open_http2_protocol = false; + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2"); + port->open_http2_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|websocket"); + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_mqtt_protocol = true; + ASSERT_STREQ(port->get_protocols(), "mqtt"); + port->open_mqtt_protocol = false; + + port->open_redis_protocol = true; + ASSERT_STREQ(port->get_protocols(), "redis"); + port->open_redis_protocol = false; + + port->clear_protocol(); + ASSERT_EQ(port->open_eof_check, 0); + ASSERT_EQ(port->open_length_check, 0); + ASSERT_EQ(port->open_http_protocol, 0); + ASSERT_EQ(port->open_websocket_protocol, 0); + ASSERT_EQ(port->open_http2_protocol, 0); + ASSERT_EQ(port->open_mqtt_protocol, 0); + ASSERT_EQ(port->open_redis_protocol, 0); + ASSERT_STREQ(port->get_protocols(), "raw"); +} + +TEST(server, pipe_message) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + + server->create(); + server->onPipeMessage = [](Server *serv, EventData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + return SW_OK; + }; + + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + server->onWorkerStart = [&](Server *server, Worker *worker) { + if (worker->id == 1) { + EventData buf{}; + string data = string(packet); + + memset(&buf.info, 0, sizeof(buf.info)); + ASSERT_TRUE(Server::task_pack(&buf, data.c_str(), data.length())); + buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; + + Worker *to_worker = server->get_worker(worker->id - 1); + server->send_to_worker_from_worker( + to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); + sleep(1); + + kill(server->get_master_pid(), SIGTERM); + } + }; + + server->start(); +} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 00000000000..55ef9fd9874 --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,145 @@ +# Swoole Changelog + +## 2024-12-16 v6.0.0 +# ✨ New Feature: +- Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. +- Added a new thread class `Swoole\Thread`. @matyhtf +- Introduced thread lock `Swoole\Thread\Lock`. @matyhtf +- Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf +- Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf +- 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 +- Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman +- Added `Swoole\Thread\Map::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf +- Added `Swoole\Process::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setName()` method. @matyhtf +- Added `Swoole\Thread::setAffinity()` method. @matyhtf +- Added `Swoole\Thread::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setPriority()` method. @matyhtf +- Added `Swoole\Thread::getPriority()` method. @matyhtf +- Added `Swoole\Thread::gettid()` method. +- The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman +- Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman +- Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman +- Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf +- Added new function and class to set cookies. @matyhtf @NathanFreeman +- Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman +- `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf +- `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf +- Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf +- Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf +- 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 + +# 🐛 Bug Fixed: +- Fixed the issue where installation via `pecl` was not possible. @remicollet +- Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman +- Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman +- Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf +- Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf +- Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman +- Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward +- Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf +- Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf +- Fixed the occasional unreachable branch issue during file upload. @NathanFreeman +- Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman +- Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk +- Capture exceptions when thread creation fails. @matyhtf +- Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk +- Fixed the fatal compile error with `GCC 14`. @remicollet +- Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng +- Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman +- Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf +- Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf +- Fixed incorrect `content-type` when uploading files. @matyhtf +- Fixed code errors in the `http2` coroutine client. @matyhtf +- Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad +- Fixed errors related to `brotli` in `config.m4`. @fundawang +- Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf +- Fixed compilation errors in the `macos` environment. @matyhtf +- Fixed the issue of threads not being able to exit safely. @matyhtf +- 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 +- Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf +- Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet +- Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf +- Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf +- Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf +- Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf +- Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf +- Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf +- Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf +- Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + +# ⭐️ Kernel optimization: +- Removed unnecessary checks for `socket structs`. @petk +- Upgraded Swoole Library. @deminy +- Added support for status code 451 in `Swoole\Http\Response`. @abnegate +- Synchronized `file` operation code across different PHP versions. @NathanFreeman +- Synchronized `pdo` operation code across different PHP versions. @NathanFreeman +- Optimized the code for `Socket::ssl_recv()`. @matyhtf +- Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman +- Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman +- Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf +- Optimized some fundamental coroutine logic. @matyhtf +- Upgraded the Oracle database version for CI testing. @gvenzl +- Optimized the underlying logic of `sendfile`. @matyhtf +- Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk +- Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf +- Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf +- Enhanced the HTTP client to accept duplicate request headers. @matyhtf +- Optimized `Swoole\Http\Response::write()`. @matyhtf +- `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf +- Compatible with `PHP 8.4`. @matyhtf @NathanFreeman +- Added the ability for asynchronous writing at the underlying socket level. @matyhtf +- Optimized `Swoole\Http\Response`. @NathanFreeman +- Improved underlying error messages. @matyhtf +- Supported sharing PHP native sockets in multi-threaded mode. @matyhtf +- Optimized static file service and fixed static file path error issues. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf +- Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman +- Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman +- Optimized timers so that synchronous processes do not depend on signals. @matyhtf +- Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + +# ❌ Deprecated: +- No longer supports `PHP 8.0`. +- No longer supports `Swoole\Coroutine\MySQL` coroutine client. +- No longer supports `Swoole\Coroutine\Redis` coroutine client. +- No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. +- Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. +## 2024-01-24 v5.1.2 +- Added support for embed sapi @matyhtf +- Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft +- Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft +- Optimized HTTP server performance @NathanFreeman +- Fixed truncated cookie @stnguyen90 +- Fixed native-curl crash on PHP 8.3 @NathanFreeman +- Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad +- Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin +- Fixed HTTP2 Typo @leocavalcante + +## 2022-07-22 v5.0.0 + +### Added +* Added `max_concurrency` option for `Server` +* Added `max_retries` option for `Coroutine\Http\Client` +* Added `name_resolver` global option +* Added `upload_max_filesize` option for `Server` +* Added `Coroutine::getExecuteTime()` +* Added `SWOOLE_DISPATCH_CONCURRENT_LB` dispatch_mode for `Server` + +### Changed +* Enhanced type system, added types for parameters and return values of all functions +* Optimized error handling, all constructors will throw exceptions when fail +* Adjusted the default mode of Server, the default is `SWOOLE_BASE` mode + +### Removed + +- Removed `PSR-0` style class names +- Removed the automatic addition of `Event::wait()` in shutdown function +- Removed `Server::tick/after/clearTimer/defer` aliases +- Removed `--enable-http`/`--enable-swoole-json`, adjusted to be enable by default + +### Deprecated +- Deprecated `Coroutine\Redis` and `Coroutine\MySQL` diff --git a/CODE-STYLE.md b/docs/CODE-STYLE.md similarity index 100% rename from CODE-STYLE.md rename to docs/CODE-STYLE.md diff --git a/CPPLINT.cfg b/docs/CPPLINT.cfg similarity index 100% rename from CPPLINT.cfg rename to docs/CPPLINT.cfg diff --git a/docs/SUPPORTED.md b/docs/SUPPORTED.md new file mode 100644 index 00000000000..80266880b63 --- /dev/null +++ b/docs/SUPPORTED.md @@ -0,0 +1,35 @@ +## Supported Versions + +| Branch | PHP Version | Initialization | Active Support Until | Security Support Until | +|-----------------------------------------------------------------|-------------|----------------|----------------------|------------------------| +| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.3 | 2023-11-29 | 2024-11-29 | 2025-04-29 | +| [v6.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4 | 2024-12-31 | 2025-12-31 | 2026-06-31 | + + +- **Active support**: A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. +- **Security fixes only**: A release that is supported for critical security issues only. Releases are only made on an as-needed basis. + +## PHP Version Support + +1. 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. +2. 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. +3. No support for any DEV or RC stage of PHP + +The 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. + + +## Unsupported Branches + +> 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. + + +| Branch | PHP Version | Duration | +|----------------------------|-------------|----------------------------------| +| `1.x` | 5.4 - 7.2 | 2012-7-1 ~ 2018-05-14 | +| `2.x` | 7.0 - 7.3 | 2016-12-30 ~ 2018-05-23 | +| `4.0.x` ~ `4.3.x` | 7.0 - 7.4 | 2018-06-14 ~ 2019-12-31 | +| `4.4.x` | 7.1 - 7.4 | 2019-04-15 ~ 2022-07-31 | +| `4.5.x`,`4.6.x`, `4.7.x` | 7.1 - 7.4 | 2019-12-20 ~ 2021-12-31 | +| `4.8.x` | 7.3 - 8.2 | 2021-10-14 ~ 2024-06-30 | +| `5.0.x` | 7.4 - 8.3 | 2022-01-20 ~ 2023-07-20 | + diff --git a/docs/TESTS.md b/docs/TESTS.md new file mode 100644 index 00000000000..d88b7595cbc --- /dev/null +++ b/docs/TESTS.md @@ -0,0 +1,73 @@ +## Swoole Tests + +## Core Tests +Used to test the core code in the `src/` directory, only as a C++ library, not related to php. +The core tests depends on the googletest framework, and googletest needs to be installed. +The core test cases must be written with `C++`. + +### Build libswoole.so + +```shell +cd swoole-src +cmake . +make -j$(nproc) +``` + +### Export source directory +```shell +SWOOLE_DIR=/your/path/to/swoole-src +``` + +### Build core-tests +```shell +cd core-tests +cmake . +make -j$(nproc) +``` + +### Run core-tests +```shell +# run all tests +./bin/core-tests +# run some test cases +./bin/core-tests --gtest_filter=server.* +# list all tests +./bin/core_tests --gtest_list_tests +``` + +## PHP Tests +Used to test the php extension code in the `ext-src/` directory. The swoole php test depends on php environment. +The `php-dev` related components must be installed. + +The php test cases must be written with `PHP`. + +### Build ext-swoole +```shell +cd swoole-src +phpize +./configure ${options} +make -j$(nproc) +make install +``` +Need to configure `php.ini`, add `extension=swoole.so` to enable `ext-swoole`. + +### Run tests +```shell +./scripts/route.sh +``` + +The 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. + +### With special branch + +```shell +SWOOLE_BRANCH=alpine ./scripts/route.sh +``` + +### Enter the container + +> You can cancel the unit test by `CTRL+C` + +```shell +docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash +``` diff --git a/google-style.xml b/docs/google-style.xml similarity index 100% rename from google-style.xml rename to docs/google-style.xml diff --git a/docs/sponsors.md b/docs/sponsors.md new file mode 100644 index 00000000000..27bbfb7c100 --- /dev/null +++ b/docs/sponsors.md @@ -0,0 +1,81 @@ +
+ +# [成为 Swoole 的赞助者](#title1) +--- + +Swoole 是采用 Apache 许可的开源项目,是完全免费的。 + +维护这样一个庞大的生态系统和为项目开发新功能所需的巨大努力,只有在我们的赞助者慷慨的财务支持下才得以持续。 +捐赠募集到的资金将全部用于开源项目的维护和发展。包括但不限于: +- 向核心贡献者支付报酬 +- 服务器租赁费用 +- 网站和文档的维护 +- 举办线下活动 +- 为社区活跃者提供奖励 + +> 您可以扫码添加微信客服或通过邮件 `service@swoole.com` 与我们联系,沟通赞助事宜 + +
+ +## [赞助渠道](#title2) + +--- +来自世界各地的企业可以通过赞助来支持 `Swoole` 项目的开发。 +根据赞助商赞助金额的不同分为顶级赞助商、金牌赞助商,在获得捐赠的第一年内我们将为赞助商做以下品牌展示: + +> 顶级赞助商 +- 官网、文档、问答社区首屏顶部显著位置展示 +- GitHub 仓库首页顶部赞助商标识 +- 官方微信公众号、QQ 群、微博、知乎等官方社交平台赞助商宣传内容推送 + +> 金牌赞助商 +- 官网、文档、问答社区首屏侧边位置展示 + +## 顶级赞助商 + + + + +## 金牌赞助商 + + + + + + + + + +## 银牌赞助商 + + + + + + + + diff --git a/swoole-logo.svg b/docs/swoole-logo.svg similarity index 100% rename from swoole-logo.svg rename to docs/swoole-logo.svg diff --git a/examples/atomic/test.php b/examples/atomic/test.php index 10b9bf40f83..6afe709eca7 100644 --- a/examples/atomic/test.php +++ b/examples/atomic/test.php @@ -1,5 +1,5 @@ add(12)."\n"; echo $atomic->sub(11)."\n"; echo $atomic->cmpset(122, 999)."\n"; diff --git a/examples/atomic/wait.php b/examples/atomic/wait.php index a4b4048fc2e..26a56364a07 100644 --- a/examples/atomic/wait.php +++ b/examples/atomic/wait.php @@ -1,5 +1,5 @@ 0) { diff --git a/examples/c10k.php b/examples/c10k.php deleted file mode 100644 index 1638ff448c1..00000000000 --- a/examples/c10k.php +++ /dev/null @@ -1,28 +0,0 @@ - 0) - { - continue; - } - else - { - for($i = 0; $i < 9999; $i++){ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 - $ret = $client->connect('127.0.0.1', 9501, 0.5); - if(!$ret) - { - echo "#$i\tConnect fail. errno=".$client->errCode; - die("\n"); - } - $clients[] = $client; - usleep(10); - } - echo "Worker #".posix_getpid()." connect $i finish\n"; - sleep(1000); - exit; - } -} -sleep(1000); diff --git a/examples/client/async.php b/examples/client/async.php deleted file mode 100644 index 83de9fbd88f..00000000000 --- a/examples/client/async.php +++ /dev/null @@ -1,53 +0,0 @@ -set(array( -// 'socket_buffer_size' => 1024 * 1024 * 2, -// 'open_eof_check' => true, -// 'package_eof' => "\r\n\r\n", -//)); - -$client->_count = 0; -$client->on("connect", function(swoole_client $cli) { - //swoole_timer_clear($cli->timer); - $cli->send("GET / HTTP/1.1\r\n\r\n"); - //$cli->sendfile(__DIR__.'/test.txt'); - //$cli->_count = 0; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "Receive: $data"; - $cli->_count++; - if ($cli->_count > 5) - { - //睡眠模式,不再接收新的数据 - echo "count=10, sleep(5000ms)\n"; - $cli->sleep(); - $cli->_count = 0; - swoole_timer_after(5000, function() use ($cli) { - //唤醒 - $cli->wakeup(); - }); - //$cli->close(); - return; - } - else - { - $cli->send(str_repeat('A', 100)."\n"); - } -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -//$client->timer = swoole_timer_after(1000, function () use ($client) { -// echo "socket timeout\n"; -// $client->close(); -//}); - -//echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/client/c10k.php b/examples/client/c10k.php new file mode 100644 index 00000000000..7f4423bd766 --- /dev/null +++ b/examples/client/c10k.php @@ -0,0 +1,23 @@ + 0) { + continue; + } else { + for ($i = 0; $i < 9999; $i++) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $ret = $client->connect('127.0.0.1', 9501, 0.5); + if (!$ret) { + echo "#$i\tConnect fail. errno=" . $client->errCode; + die("\n"); + } + $clients[] = $client; + usleep(10); + } + echo "Worker #" . posix_getpid() . " connect $i finish\n"; + sleep(1000); + exit; + } +} +sleep(1000); diff --git a/examples/client/get_socket.php b/examples/client/get_socket.php index 9b23d9d7543..4fa0eff243d 100644 --- a/examples/client/get_socket.php +++ b/examples/client/get_socket.php @@ -2,7 +2,7 @@ function getClient() { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); @@ -17,7 +17,7 @@ function getClient() $count = 0; //$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) diff --git a/examples/client/long_tcp.php b/examples/client/long_tcp.php index 69aaaef9683..1667ed0d235 100644 --- a/examples/client/long_tcp.php +++ b/examples/client/long_tcp.php @@ -1,7 +1,7 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/client/recv_1m.php b/examples/client/recv_1m.php new file mode 100644 index 00000000000..82d3f8bded9 --- /dev/null +++ b/examples/client/recv_1m.php @@ -0,0 +1,27 @@ +connect('127.0.0.1', 9509, 60); +$c->send("AAAAAAAAAAAAAAAA"); + +$n_bytes = 0; + +while (true) +{ + $line = $c->recv(); + if ($line === false) + { + echo "recv failed.\n"; + break; + } + elseif (empty($line)) + { + echo "recv $n_bytes bytes\n"; + break; + } + else + { + fwrite($f, $line); + $n_bytes += strlen($line); + } +} diff --git a/examples/client/recv_file.php b/examples/client/recv_file.php new file mode 100644 index 00000000000..1af8d443d57 --- /dev/null +++ b/examples/client/recv_file.php @@ -0,0 +1,36 @@ +connect($server_ip, 9501, 5); +$filesize = intval($cli->recv()); +if ($filesize == 0) { + die("get file size failed.\n"); +} +echo "file_size = $filesize\n"; +$content = ''; +$cli->send("get file"); + +$use_waitall = false; + +if ($use_waitall) { + //waitall,需要一次性分配内存,适合小一点的文件 + $content = $cli->recv($filesize, true); +} else { + //循环接收,适合大型文件 + while (1) { + //超大文件接收,这里需要改成分段写磁盘 + $content .= $cli->recv(); + if (strlen($content) == $filesize) { + break; + } + } +} +file_put_contents(__DIR__ . "/recv_file_" . time() . ".jpg", $content); +echo "recv " . strlen($content) . " byte data\n"; +echo "used " . ((microtime(true) - $start_ms) * 1000) . "ms\n"; +$cli->close(); diff --git a/examples/client/select.php b/examples/client/select.php index be4ce20d92f..98d23a43dd1 100644 --- a/examples/client/select.php +++ b/examples/client/select.php @@ -1,30 +1,23 @@ connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Connect Server fail.errCode=".$client->errCode; - } - else - { - $client->send("HELLO WORLD\n"); - $clients[$client->sock] = $client; + if (!$ret) { + echo "Connect Server fail.errCode=" . $client->errCode; + } else { + $client->send("HELLO WORLD\n"); + $clients[$client->sock] = $client; } } -while (!empty($clients)) -{ +while (!empty($clients)) { $write = $error = array(); $read = array_values($clients); $n = swoole_client_select($read, $write, $error, 0.6); - if ($n > 0) - { - foreach ($read as $index => $c) - { + if ($n > 0) { + foreach ($read as $index => $c) { echo "Recv #{$c->sock}: " . $c->recv() . "\n"; unset($clients[$c->sock]); } diff --git a/examples/client/simple.php b/examples/client/simple.php new file mode 100644 index 00000000000..2756cdc435e --- /dev/null +++ b/examples/client/simple.php @@ -0,0 +1,20 @@ +connect('127.0.0.1', 9501, 0.5, 0); + if (!$ret) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + $clients[] = $client; +} +sleep(1); +while (1) { + foreach ($clients as $client) { + $client->send("sss"); + $data = $client->recv(); + var_dump($data); + } + sleep(1); +} diff --git a/examples/client/sync.php b/examples/client/sync.php index f251eb1357a..d93713d21fc 100644 --- a/examples/client/sync.php +++ b/examples/client/sync.php @@ -1,14 +1,13 @@ set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) do_connect: -if (!$client->connect('127.0.0.1', 9501, -1)) -{ +if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } @@ -23,7 +22,6 @@ $client->close(); $count++; -if ($count < 20) -{ +if ($count < 20) { goto do_connect; } diff --git a/examples/client/udp_async.php b/examples/client/udp_async.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/client/udp_async.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/client/udp_sync.php b/examples/client/udp_sync.php index 03c2847fcfb..55eb8507fc1 100644 --- a/examples/client/udp_sync.php +++ b/examples/client/udp_sync.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9502); for ($i = 0; $i < 100; $i++) diff --git a/examples/client2.php b/examples/client2.php deleted file mode 100644 index e7671133774..00000000000 --- a/examples/client2.php +++ /dev/null @@ -1,21 +0,0 @@ -connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Over flow. errno=".$client->errCode; - die("\n"); - } - $clients[] = $client; -} -sleep(1); -while (1) { - foreach ($clients as $client) { - $client->send("sss"); - $data = $client->recv(); - var_dump($data); - } - sleep(1); -} diff --git a/examples/coroutine/TestHttpServ.php b/examples/coroutine/TestHttpServ.php deleted file mode 100644 index d2f796b7d6e..00000000000 --- a/examples/coroutine/TestHttpServ.php +++ /dev/null @@ -1,117 +0,0 @@ -setting = $setting; - } - - /** - * [init description] - * @return [type] [description] - */ - public function init(){ - - if (!isset($this ->setting['host'])) { - $this ->setting['host'] = '0.0.0.0'; - } - if (!isset($this ->setting['port'])) { - $this ->setting['port'] = '9999'; - } - - $this ->http = new swoole_http_server($this ->setting['host'], $this ->setting['port']); - $this ->http ->set($this ->setting); - - $this ->http ->on('request', array($this, 'onRequest')); - $this ->http ->on('close', array($this, 'onClose')); - } - - /** - * [onRequest description] - * @param [type] $request [description] - * @param [type] $response [description] - * @return [type] [description] - */ - public function onRequest($request, $response){ - - // $udp = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_ASYNC); - // $udp->on("connect", function(swoole_client $cli) { - // $cli->send("udp test"); - // }); - // $udp->on("receive", function(swoole_client $cli, $data)use($response){ - - $tcp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $tcp->on("connect", function(swoole_client $cli) { - $cli->send("tcp test"); - }); - $tcp->on("receive", function(swoole_client $cli, $data)use($response){ - $response ->end("

swoole response

"); - }); - $tcp->on("close", function(swoole_client $cli){ - }); - $tcp->on("error", function(swoole_client $cli){ - }); - $tcp->connect('10.100.64.151', 9805); - - // }); - // $udp->on("close", function(swoole_client $cli){ - // }); - // $udp->connect('10.100.65.222', 9906); - - } - - /** - * [onClose description] - * @param [type] $server [description] - * @param [type] $fd [description] - * @param [type] $reactor_id [description] - * @return [type] [description] - */ - public function onClose($server, $fd, $reactor_id){ - - //echo " on close fd = $fd reactor_id = $reactor_id \n"; - } - - /** - * [start description] - * @return [type] [description] - */ - public function start(){ - - $this ->init(); - $this ->http ->start(); - } -} - -$setting = array( - 'host' => '0.0.0.0', - 'port' => 10005, - 'worker_num' => 4, - 'dispatch_mode' => 2, //固定分配请求到worker - 'reactor_num' => 4, //亲核 - 'daemonize' => 1, //守护进程 - 'backlog' => 128, - 'log_file' => '/data/log/test_http_server.log', -); -$th = new TestHttpServer(); -$th ->set($setting); -$th ->start(); diff --git a/examples/coroutine/before_server_start.php b/examples/coroutine/before_server_start.php index 5cac5fab427..4c9b5743ec6 100644 --- a/examples/coroutine/before_server_start.php +++ b/examples/coroutine/before_server_start.php @@ -3,7 +3,7 @@ { co::sleep(1); - $http = new swoole_http_server("127.0.0.1", 9501, SWOOLE_BASE); + $http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); $http->on("start", function ($server) { diff --git a/examples/coroutine/channel/test.php b/examples/coroutine/channel/test.php index 0ea427374c2..582fb191bea 100644 --- a/examples/coroutine/channel/test.php +++ b/examples/coroutine/channel/test.php @@ -15,7 +15,7 @@ function BatchExecMethodByCo() $list[$key] = $channel->pop(); } }); - swoole_event_wait(); + Swoole\Event::wait(); return $list; } function test($value='') @@ -29,4 +29,4 @@ function test2($value='') return "test2 ".rand(1,10)."\n"; } $r = BatchExecMethodByCo("test","test2","test"); -var_dump($r); \ No newline at end of file +var_dump($r); diff --git a/examples/coroutine/client_send_yield.php b/examples/coroutine/client_send_yield.php index 87e955c2f13..edad3ee8b4d 100644 --- a/examples/coroutine/client_send_yield.php +++ b/examples/coroutine/client_send_yield.php @@ -24,4 +24,4 @@ var_dump($client->errCode); }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/coroutine/coro_channel.php b/examples/coroutine/coro_channel.php index 18938ce5983..16052d13491 100644 --- a/examples/coroutine/coro_channel.php +++ b/examples/coroutine/coro_channel.php @@ -1,14 +1,14 @@ set(array( 'log_file' => '/dev/null' )); use Swoole\Coroutine as co; -// $http->on("WorkerStart", function (\swoole_server $serv) +// $http->on("WorkerStart", function (\Swoole\Server $serv) // { // // }); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $ch = new co\Channel(1); $out = new co\Channel(1); diff --git a/examples/coroutine/coro_sleep.php b/examples/coroutine/coro_sleep.php index 50468f7e791..e89af375ba1 100644 --- a/examples/coroutine/coro_sleep.php +++ b/examples/coroutine/coro_sleep.php @@ -7,3 +7,4 @@ echo "OK\n"; }); echo "11\n"; +Swoole\Event::wait(); diff --git a/examples/coroutine/csp.php b/examples/coroutine/csp.php index 464b7e4e4dd..015f0f9198e 100644 --- a/examples/coroutine/csp.php +++ b/examples/coroutine/csp.php @@ -1,5 +1,5 @@ on('request', function ($req, $resp) { $chan = new chan(2); diff --git a/examples/coroutine/enable_coroutine.php b/examples/coroutine/enable_coroutine.php index 4fb53f46f37..17e24077e69 100644 --- a/examples/coroutine/enable_coroutine.php +++ b/examples/coroutine/enable_coroutine.php @@ -3,7 +3,7 @@ use Swoole\Http\Request; use Swoole\Http\Response; -$http = new swoole_http_server('127.0.0.1', 9501); +$http = new Swoole\Http\Server('127.0.0.1', 9501); $http->set([ 'enable_coroutine' => false, // close build-in coroutine diff --git a/examples/coroutine/exit_with_status.php b/examples/coroutine/exit_with_status.php index df77d724d30..ae723bbcb08 100644 --- a/examples/coroutine/exit_with_status.php +++ b/examples/coroutine/exit_with_status.php @@ -8,5 +8,5 @@ $exit_status = $e->getStatus(); } }); -swoole_event_wait(); +Swoole\Event::wait(); exit($exit_status); diff --git a/examples/coroutine/http/server.php b/examples/coroutine/http/server.php index 0c8cc2b8da6..05928dafdd5 100644 --- a/examples/coroutine/http/server.php +++ b/examples/coroutine/http/server.php @@ -50,4 +50,4 @@ $server->start(); }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/coroutine/http/write_func.php b/examples/coroutine/http/write_func.php new file mode 100644 index 00000000000..e8beabc6e49 --- /dev/null +++ b/examples/coroutine/http/write_func.php @@ -0,0 +1,14 @@ + SWOOLE_TRACE_HTTP2, + 'log_level' => 0, +]); +Co\run(function () { + $client = new Swoole\Coroutine\Http\Client('www.jd.com', 443, true); + $client->set(['write_func' => function($client, $data) { + var_dump(strlen($data)); + }]); + $client->get('/'); + var_dump(strlen($client->getBody())); + return 0; +}); diff --git a/examples/coroutine/http_download.php b/examples/coroutine/http_download.php index d5d3d217936..391d57608fc 100644 --- a/examples/coroutine/http_download.php +++ b/examples/coroutine/http_download.php @@ -1,7 +1,10 @@ set(['timeout' => -1]); $cli->setHeaders([ 'Host' => $host, @@ -9,5 +12,5 @@ 'Accept' => '*', 'Accept-Encoding' => 'gzip' ]); - $cli->download('/static/files/swoole-logo.svg', __DIR__ . '/logo.svg'); + $cli->download('/dist/skin1/images/logo-white.png', '/tmp/logo.png'); }); diff --git a/examples/coroutine/pdo/pdo_persistent.phpt b/examples/coroutine/pdo/pdo_persistent.phpt new file mode 100644 index 00000000000..04abb7b6642 --- /dev/null +++ b/examples/coroutine/pdo/pdo_persistent.phpt @@ -0,0 +1,14 @@ + true) + ); + echo "connected\n"; + sleep(30); + echo "sleep 30\n"; + $pdo->exec("SELECT sleep(1)"); +}); + +echo "DONE\n"; diff --git a/examples/coroutine/send_yield_client.php b/examples/coroutine/send_yield_client.php index 006634ace7d..f8caa3e34af 100644 --- a/examples/coroutine/send_yield_client.php +++ b/examples/coroutine/send_yield_client.php @@ -1,5 +1,5 @@ set(array( 'kernel_socket_buffer_size' => 65536, )); diff --git a/examples/coroutine/server/tcp.php b/examples/coroutine/server/tcp.php index 41af52d1a6c..f22e44cc753 100644 --- a/examples/coroutine/server/tcp.php +++ b/examples/coroutine/server/tcp.php @@ -21,4 +21,4 @@ $server->start(); }); -swoole_event::wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/coroutine/task_co.php b/examples/coroutine/task_co.php index ad5a4e21a55..cd1e5c25fbe 100644 --- a/examples/coroutine/task_co.php +++ b/examples/coroutine/task_co.php @@ -6,7 +6,7 @@ 'task_worker_num' => 2, ]); -$server->on('Task', function (swoole_server $serv, $task_id, $worker_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; if ($serv->worker_id == 1) { sleep(1); @@ -14,7 +14,7 @@ return $data; }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/coroutine/tcp_echo.php b/examples/coroutine/tcp_echo.php index 02da4c53b65..805d6f7b861 100644 --- a/examples/coroutine/tcp_echo.php +++ b/examples/coroutine/tcp_echo.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd]: Connect.\n"; }); @@ -8,7 +8,7 @@ )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "{$data}\n") == false) { diff --git a/examples/coroutine/timer_test.php b/examples/coroutine/timer_test.php index 472c44022d3..9d8a1bb93fc 100755 --- a/examples/coroutine/timer_test.php +++ b/examples/coroutine/timer_test.php @@ -6,11 +6,11 @@ * @Last Modified time: 2016-06-26 16:41:46 */ -swoole_timer_after(1000, function(){ +Swoole\Timer::after(1000, function(){ echo " timer after timeout\n"; }); -swoole_timer_tick(1000, function(){ +Swoole\Timer::tick(1000, function(){ echo "timer tick timeout\n"; }); ?> diff --git a/examples/coroutine/udp_client.php b/examples/coroutine/udp_client.php index 87dc798f4eb..eff7508b268 100755 --- a/examples/coroutine/udp_client.php +++ b/examples/coroutine/udp_client.php @@ -15,7 +15,7 @@ public function sendRequest() public function send() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send($this->data); $ret = $cli->recv(); @@ -24,15 +24,15 @@ public function send() public function moreThanOneRecv() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send("sent by cli"); - $cli2 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli2 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli2->connect($this->ip, self::PORT); $cli2->send("sent by cli2"); - $cli3 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli3 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli3->connect($this->ip, self::PORT); $cli3->send("sent by cli3"); @@ -50,7 +50,7 @@ class Server public function run() { - $this->server = new swoole_http_server("127.0.0.1", 9502); + $this->server = new Swoole\Http\Server("127.0.0.1", 9502); $this->server->set([ 'worker_num' => 1, 'daemonize' => true, @@ -63,7 +63,7 @@ public function run() public static function onRequest($request, $response) { self::staticFunc(); - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $client = new Client(); $ret = $client->sendRequest(); $response->end($ret); diff --git a/examples/coroutine/websocket/server.php b/examples/coroutine/websocket/server.php index 8c245d2b23c..ec5fbd43142 100644 --- a/examples/coroutine/websocket/server.php +++ b/examples/coroutine/websocket/server.php @@ -1,13 +1,13 @@ set(array( 'log_file' => '/dev/null' )); -$ws->on("WorkerStart", function (\swoole_server $serv) { +$ws->on("WorkerStart", function (\Swoole\Server $serv) { }); -$ws->on('open', function ($serv, swoole_http_request $request) { +$ws->on('open', function ($serv, Swoole\Http\Request $request) { //$ip = co::gethostbyname('www.baidu.com'); if (1) { $serv->push($request->fd, "start\n"); diff --git a/examples/cpp/Makefile b/examples/cpp/Makefile index b5663c64647..b3bc0fab6ce 100644 --- a/examples/cpp/Makefile +++ b/examples/cpp/Makefile @@ -1,8 +1,10 @@ -all: co repeat +all: co repeat test_server co: co.cc g++ co.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o co -g repeat: repeat.cc g++ repeat.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o repeat -g +test_server: test_server.cc + g++ test_server.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o test_server -g clean: - rm -f co repeat + rm -f co repeat test_server diff --git a/examples/cpp/co.cc b/examples/cpp/co.cc index 8ba86cb7d10..ffd18c82b05 100644 --- a/examples/cpp/co.cc +++ b/examples/cpp/co.cc @@ -17,94 +17,95 @@ list slaves; size_t qs; int main(int argc, char **argv) { - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - signal(SIGPIPE, SIG_IGN); - Coroutine::create([](void *arg) { - System::sleep(2.0); - cout << "CO-1, sleep 2\n"; - }); + Coroutine::run([](void *arg) { + Coroutine::create([](void *arg) { + System::sleep(2.0); + cout << "CO-1, sleep 2\n"; + }); - Coroutine::create([](void *arg) { - System::sleep(1); - cout << "CO-2, sleep 1\n"; - }); + Coroutine::create([](void *arg) { + System::sleep(1); + cout << "CO-2, sleep 1\n"; + }); - Coroutine::create([](void *arg) { - cout << "CO-3, listen tcp:0.0.0.0:9001\n"; - Socket s(SW_SOCK_TCP); - s.bind("0.0.0.0", 9001); - s.listen(); + Coroutine::create([](void *arg) { + cout << "CO-3, listen tcp:0.0.0.0:9001\n"; + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9001); + s.listen(); - while (1) { - Socket *_client = s.accept(); - Coroutine::create( - [](void *arg) { - Socket *client = (Socket *) arg; - while (1) { - char buf[1024]; - auto retval = client->recv(buf, sizeof(buf)); - if (retval == 0) { - cout << "connection close\n"; - break; - } else { - if (strncasecmp("push", buf, 4) == 0) { - q.push_back(string(buf + 5, retval - 5)); - qs += retval - 5; - string resp("OK\n"); - client->send(resp.c_str(), resp.length()); + while (1) { + Socket *_client = s.accept(); + Coroutine::create( + [](void *arg) { + Socket *client = (Socket *) arg; + while (1) { + char buf[1024]; + auto retval = client->recv(buf, sizeof(buf)); + if (retval == 0) { + cout << "connection close\n"; + break; + } else { + if (strncasecmp("push", buf, 4) == 0) { + q.push_back(string(buf + 5, retval - 5)); + qs += retval - 5; + string resp("OK\n"); + client->send(resp.c_str(), resp.length()); - for (auto it = slaves.begin(); it != slaves.end();) { - auto sc = *it; - auto n = sc->send(buf, retval); - if (n <= 0) { - it = slaves.erase(it); - delete sc; + for (auto it = slaves.begin(); it != slaves.end();) { + auto sc = *it; + auto n = sc->send(buf, retval); + if (n <= 0) { + it = slaves.erase(it); + delete sc; + } else { + it++; + } + } + } else if (strncasecmp("pop", buf, 3) == 0) { + if (q.empty()) { + string resp("EMPTY\n"); + client->send(resp.c_str(), resp.length()); } else { - it++; + auto data = q.front(); + q.pop_front(); + qs -= data.length(); + client->send(data.c_str(), data.length()); } - } - } else if (strncasecmp("pop", buf, 3) == 0) { - if (q.empty()) { - string resp("EMPTY\n"); - client->send(resp.c_str(), resp.length()); + } else if (strncasecmp("stat", buf, 4) == 0) { + char stat_buf[64]; + int n = snprintf(stat_buf, sizeof(stat_buf), "count=%ld,bytes=%ld\n", q.size(), qs); + client->send(stat_buf, n); } else { - auto data = q.front(); - q.pop_front(); - qs -= data.length(); - client->send(data.c_str(), data.length()); + string resp("ERROR\n"); + client->send(resp.c_str(), resp.length()); } - } else if (strncasecmp("stat", buf, 4) == 0) { - char stat_buf[64]; - int n = snprintf(stat_buf, sizeof(stat_buf), "count=%ld,bytes=%ld\n", q.size(), qs); - client->send(stat_buf, n); - } else { - string resp("ERROR\n"); - client->send(resp.c_str(), resp.length()); } } - } - delete client; - }, - _client); - } - }); + delete client; + }, + _client); + } + }); - Coroutine::create([](void *arg) { - Socket s(SW_SOCK_TCP); - s.bind("0.0.0.0", 9002); - s.listen(); - while (1) { - Socket *_client = s.accept(); - for (auto data : q) { - _client->send(data.c_str(), data.length()); + Coroutine::create([](void *arg) { + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9002); + s.listen(); + while (1) { + Socket *_client = s.accept(); + if (_client == nullptr) { + break; + } + for (auto data : q) { + _client->send(data.c_str(), data.length()); + } + slaves.push_back(_client); } - slaves.push_back(_client); - } + }); }); - swoole_event_wait(); - return 0; } diff --git a/examples/cpp/repeat.cc b/examples/cpp/repeat.cc index fb4587ef458..6d79537be48 100644 --- a/examples/cpp/repeat.cc +++ b/examples/cpp/repeat.cc @@ -21,8 +21,8 @@ int main(int argc, char **argv) { serv.onPacket = [](Server *serv, RecvData *req) { return SW_OK; }; - serv.onWorkerStart = [](Server *serv, int worker_id) { - swoole_notice("WorkerStart[%d]PID=%d, serv=%p,", worker_id, getpid(), serv); + serv.onWorkerStart = [](Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d, serv=%p,", worker->id, getpid(), serv); swoole_timer_after( 1000, [serv](Timer *, TimerNode *tnode) { diff --git a/examples/cpp/test_server.cc b/examples/cpp/test_server.cc index fe3fca106b4..32e44d2c826 100644 --- a/examples/cpp/test_server.cc +++ b/examples/cpp/test_server.cc @@ -14,8 +14,8 @@ void my_onStart(Server *serv); void my_onShutdown(Server *serv); void my_onConnect(Server *serv, DataHead *info); void my_onClose(Server *serv, DataHead *info); -void my_onWorkerStart(Server *serv, int worker_id); -void my_onWorkerStop(Server *serv, int worker_id); +void my_onWorkerStart(Server *serv, Worker *worker); +void my_onWorkerStop(Server *serv, Worker *worker); static int g_receive_count = 0; @@ -83,12 +83,12 @@ int main(int argc, char **argv) { return 0; } -void my_onWorkerStart(Server *serv, int worker_id) { - swoole_notice("WorkerStart[%d]PID=%d", worker_id, getpid()); +void my_onWorkerStart(Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d", worker->id, worker->pid); } -void my_onWorkerStop(Server *serv, int worker_id) { - swoole_notice("WorkerStop[%d]PID=%d", worker_id, getpid()); +void my_onWorkerStop(Server *serv, Worker *worker) { + swoole_notice("WorkerStop[%d]PID=%d", worker->id, worker->pid); } int my_onReceive(Server *serv, RecvData *req) { diff --git a/examples/dtls/server.php b/examples/dtls/server.php index b79ac2053a9..d6b8e4132aa 100644 --- a/examples/dtls/server.php +++ b/examples/dtls/server.php @@ -1,5 +1,5 @@ set( [ @@ -12,7 +12,7 @@ ] ); -$server->on('Receive', function (swoole_server $serv, $fd, $tid, $data) +$server->on('Receive', function (Swoole\Server $serv, $fd, $tid, $data) { var_dump($fd, $data, $serv->getClientInfo($fd)); $serv->send($fd, "Swoole: $data\n"); diff --git a/examples/eof/async_client.php b/examples/eof/async_client.php deleted file mode 100644 index e935cb86d18..00000000000 --- a/examples/eof/async_client.php +++ /dev/null @@ -1,35 +0,0 @@ -send($_send); - echo "send ".strlen($_send)." bytes\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 -$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - static $i = 0; - if ($i % 100 == 1) - { - echo "received " . strlen($data) . " bytes\n"; - } - $i ++; - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/eof/client.php b/examples/eof/client.php index cad67406525..a013c99a279 100644 --- a/examples/eof/client.php +++ b/examples/eof/client.php @@ -2,33 +2,28 @@ /** * 分段发送数据 * - * @param swoole_client $client - * @param string $data - * @param int $chunk_size + * @param Swoole\Client $client + * @param string $data + * @param int $chunk_size */ -function send_chunk(swoole_client $client, $data, $chunk_size = 1024) +function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) { - $len = strlen($data); - $chunk_num = intval($len / $chunk_size) + 1; - for ($i = 0; $i < $chunk_num; $i++) - { - if ($len < ($i + 1) * $chunk_size) - { - $sendn = $len - ($i * $chunk_size); - } - else - { - $sendn = $chunk_size; - } - $client->send(substr($data, $i * $chunk_size, $sendn)); - } + $len = strlen($data); + $chunk_num = intval($len / $chunk_size) + 1; + for ($i = 0; $i < $chunk_num; $i++) { + if ($len < ($i + 1) * $chunk_size) { + $sendn = $len - ($i * $chunk_size); + } else { + $sendn = $chunk_size; + } + $client->send(substr($data, $i * $chunk_size, $sendn)); + } } -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 -if(!$client->connect('127.0.0.1', 9501, 0.5, 0)) -{ - echo "Over flow. errno=".$client->errCode; - die("\n"); +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 +if (!$client->connect('127.0.0.1', 9501, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); } //for ($i = 0; $i < 10; $i++) @@ -39,21 +34,20 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //exit; $data = array( - 'name' => __FILE__, - 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K + 'name' => __FILE__, + 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K ); $_serialize_data = serialize($data); -$_send = $_serialize_data."__doit__"; +$_send = $_serialize_data . "__doit__"; -echo "serialize_data length=".strlen($_serialize_data)."send length=".strlen($_send)."\n"; +echo "serialize_data length=" . strlen($_serialize_data) . "send length=" . strlen($_send) . "\n"; //send_chunk($client, $_send); // -if(!$client->send($_send)) -{ - die("send failed.\n"); +if (!$client->send($_send)) { + die("send failed.\n"); } //$client->send("\r\n".substr($_serialize_data, 0, 8000)); @@ -65,9 +59,8 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //usleep(500000); -if (!$client->send("\r\n\r\n")) -{ - die("send failed.\n"); +if (!$client->send("\r\n\r\n")) { + die("send failed.\n"); } echo $client->recv(); diff --git a/examples/eof/server.php b/examples/eof/server.php index a9951f231be..bbf1ab21eef 100644 --- a/examples/eof/server.php +++ b/examples/eof/server.php @@ -1,5 +1,5 @@ set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -11,21 +11,18 @@ //$serv->on('connect', function ($serv, $fd) { // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; //}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) -{ +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo '#' . $serv->worker_id . " recv: " . strlen($data) . "\n"; - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $resp = str_repeat('A', rand(10000, 50000)) . "\r\n\r\n"; $serv->send($fd, $resp); - if ($i % 100 == 1) - { + if ($i % 100 == 1) { sleep(1); - echo "send ".strlen($resp)." bytes\n"; + echo "send " . strlen($resp) . " bytes\n"; } } }); //$serv->on('close', function ($serv, $fd) { - //echo "[#" . posix_getpid() . "]\tClient: Close.\n"; +//echo "[#" . posix_getpid() . "]\tClient: Close.\n"; //}); $serv->start(); diff --git a/examples/event/cycle.php b/examples/event/cycle.php index 4282a6f2dd9..bc4a20da260 100644 --- a/examples/event/cycle.php +++ b/examples/event/cycle.php @@ -11,3 +11,5 @@ Swoole\Event::cycle(null); }); }); + +Swoole\Event::wait(); diff --git a/examples/event/inotify.php b/examples/event/inotify.php index 683b101e245..89d96619844 100644 --- a/examples/event/inotify.php +++ b/examples/event/inotify.php @@ -5,7 +5,7 @@ //监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS $watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY); -swoole_event_add($fd, function ($fd) { +Swoole\Event::add($fd, function ($fd) { $events = inotify_read($fd); if ($events) { foreach ($events as $event) { @@ -13,3 +13,5 @@ } } }); + +Swoole\Event::wait(); diff --git a/examples/event/sockets.php b/examples/event/sockets.php index b791c9ac092..65bf34583ad 100644 --- a/examples/event/sockets.php +++ b/examples/event/sockets.php @@ -3,51 +3,48 @@ * require ./configure --enable-sockets */ +use Swoole\Event; + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); function socket_onRead($socket) { - static $i = 0; - - echo socket_read($socket, 8192)."\n"; - $i ++; - if ($i > 10) - { - echo "finish\n"; - swoole_event_del($socket); - socket_close($socket); - } - else - { - sleep(1); - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); - } + static $i = 0; + + echo socket_read($socket, 8192) . "\n"; + $i++; + if ($i > 10) { + echo "finish\n"; + Event::del($socket); + socket_close($socket); + } else { + sleep(1); + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } } function socket_onWrite($socket) { - socket_write($socket, "hi swoole"); - swoole_event_set($socket, null, null, SWOOLE_EVENT_READ); + socket_write($socket, "hi swoole"); + Event::set($socket, null, null, SWOOLE_EVENT_READ); } function socket_onConnect($socket) { - $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); - if ($err == 0) - { - echo "connect server success\n"; - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); - socket_write($socket, "first package\n"); - } - else - { - echo "connect server failed\n"; - swoole_event_del($socket); - socket_close($socket); - } + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "connect server success\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, "first package\n"); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } } -swoole_event_add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); -@socket_connect($socket, '127.0.0.1', 9501); +Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); +socket_connect($socket, '127.0.0.1', 9501); +Event::wait(); diff --git a/examples/event/stdin.php b/examples/event/stdin.php index 6ecd4d8077e..bbf93cd9b24 100644 --- a/examples/event/stdin.php +++ b/examples/event/stdin.php @@ -1,4 +1,5 @@ setData(http_build_query(['a'=>123,'b'=>"哈哈"])); -//$cli->set(['timeout' => -1]); -//$cli->setHeaders(['Host' => 'www.baidu.com']); -//$cli->set(['http_proxy_host' => '127.0.0.1', 'http_proxy_port' => 8888,]); - -$cli->setHeaders(['User-Agent' => "swoole"]); - -$cli->get('/index.php', function ($cli) -{ - var_dump($cli); -}); - -//$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { -// echo $cli->body; -// $cli->get('/index.php', function ($cli) { -// file_put_contents(__DIR__.'/t.html', $cli->body); -// $cli->download('/index.php', __DIR__.'/phpinfo.html', function ($cli) -// { -// var_dump($cli->downloadFile); -// }); -// }); -//}); diff --git a/examples/http/async_websocket.php b/examples/http/async_websocket.php deleted file mode 100644 index be19dff7dde..00000000000 --- a/examples/http/async_websocket.php +++ /dev/null @@ -1,11 +0,0 @@ -on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/http/client.php b/examples/http/client.php index d3f3ada40eb..cdb6dbd5309 100644 --- a/examples/http/client.php +++ b/examples/http/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501); //$type = 'GET'; diff --git a/examples/http/detach.php b/examples/http/detach.php index a12ff8ccd2f..ec929b4c770 100644 --- a/examples/http/detach.php +++ b/examples/http/detach.php @@ -1,5 +1,5 @@ set(['task_worker_num' => 1, 'worker_num' => 1]); diff --git a/examples/http/event-stream.php b/examples/http/event-stream.php new file mode 100644 index 00000000000..45ab4a7ecc0 --- /dev/null +++ b/examples/http/event-stream.php @@ -0,0 +1,40 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/stream') { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + go(function () use ($resp, $http) { + while (true) { + Co::sleep(1); + $http->send($resp->fd, 'data: ' . base64_encode(random_bytes(random_int(16, 128))). "\n\n"); + } + }); + } elseif ($req->server['request_uri'] == '/') { + $resp->end(<< + + +HTML + ); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/http_proxy.php b/examples/http/http_proxy.php deleted file mode 100644 index e4642f1b635..00000000000 --- a/examples/http/http_proxy.php +++ /dev/null @@ -1,25 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; -$cli->close(); - // echo $cli->body; - }); -}); - - - - -?> diff --git a/examples/http/http_proxy_auth.php b/examples/http/http_proxy_auth.php deleted file mode 100644 index e4a1c40e445..00000000000 --- a/examples/http/http_proxy_auth.php +++ /dev/null @@ -1,18 +0,0 @@ -set(array( - 'http_proxy_host' => "127.0.0.1", - 'http_proxy_port' => 33080, - 'http_proxy_user' => 'test', - 'http_proxy_password' => 'test', -)); -$cli->setHeaders([ - 'Host' => "localhost", - "User-Agent" => 'Chrome/49.0.2587.3', -]); -$cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . ", statusCode=".$cli->statusCode."\n"; - $cli->close(); - echo $cli->body; -}); diff --git a/examples/http/https_proxy.php b/examples/http/https_proxy.php deleted file mode 100644 index eabd3758ec5..00000000000 --- a/examples/http/https_proxy.php +++ /dev/null @@ -1,21 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; - echo $cli->body; - }); -}); - - -?> diff --git a/examples/http/no-compression.php b/examples/http/no-compression.php new file mode 100644 index 00000000000..b1228aa80b5 --- /dev/null +++ b/examples/http/no-compression.php @@ -0,0 +1,17 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/') { + $resp->header('Content-Encoding', ''); + $resp->end(str_repeat('A', 1024)); + } elseif ($req->server['request_uri'] == '/gzip') { + $resp->end(str_repeat('A', 1024)); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/redirect.php b/examples/http/redirect.php index 0536e3d8453..c06e6d0a167 100644 --- a/examples/http/redirect.php +++ b/examples/http/redirect.php @@ -1,5 +1,5 @@ on('request', function ($req, Swoole\Http\Response $resp) { $resp->redirect("http://www.baidu.com/", 301); diff --git a/examples/http/server.php b/examples/http/server.php index b3ca4662285..ac9dcaf2e3f 100644 --- a/examples/http/server.php +++ b/examples/http/server.php @@ -1,14 +1,16 @@ setGlobal(HTTP_GLOBAL_ALL, HTTP_GLOBAL_GET|HTTP_GLOBAL_POST|HTTP_GLOBAL_COOKIE); $http->set([ // 'daemonize' => 1, @@ -19,6 +21,8 @@ function dump($var) //'enable_port_reuse' => true, // 'http_compression' => false, 'worker_num' => 1, + 'upload_max_filesize' => 1 * 1024 * 1024 * 1024, + 'package_max_length' => 1 * 1024 * 1024, //'log_file' => __DIR__.'/swoole.log', // 'reactor_num' => 24, //'dispatch_mode' => 3, @@ -37,7 +41,7 @@ function dump($var) $http->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); -function chunk(swoole_http_request $request, swoole_http_response $response) +function chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->write("

hello world1

"); //sleep(1); @@ -46,7 +50,7 @@ function chunk(swoole_http_request $request, swoole_http_response $response) $response->end(); } -function no_chunk(swoole_http_request $request, swoole_http_response $response) +function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { /** * Cookie Test @@ -117,7 +121,11 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) $output .= "

HEADER:

" . dump($request->header); $output .= "

SERVER:

" . dump($request->server); if (!empty($request->files)) { - $output .= "

FILE:

" . dump($request->files); + $files = $request->files; + foreach ($files as &$f) { + $f['md5'] = md5_file($f['tmp_name']); + } + $output .= "

FILE:

" . dump($files); } if (!empty($request->cookie)) { $output .= "

COOKIES:

" . dump($request->cookie); @@ -131,7 +139,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) var_dump($request->post); //$response->header('X-Server', 'Swoole'); //unset($request, $response); -// swoole_timer_after(2000, function() use ( $response) { +// Swoole\Timer::after(2000, function() use ( $response) { $response->end("

Hello Swoole.

" . $output); return; // }); @@ -154,15 +162,11 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) //global $http; //$http->task("hello world"); $file = realpath(__DIR__ . '/../' . $request->server['request_uri']); - if (is_file($file)) - { + if (is_file($file)) { echo "http get file=$file\n"; - if (substr($file, -4) == '.php') - { + if (substr($file, -4) == '.php') { $response->gzip(); - } - else - { + } else { $response->header('Content-Type', 'image/jpeg'); } $content = file_get_contents($file); @@ -172,9 +176,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) // $response->end(); $response->end($content); - } - else - { + } else { $response->end("

Hello Swoole.

"); } } @@ -182,23 +184,20 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) $http->on('request', function ($req, $resp) { $uri = $req->server['request_uri']; if ($uri == '/favicon.ico') { - $resp->status(404); + $resp->status(404); $resp->end(); - } - elseif ($uri == '/chunk') { - chunk($req, $resp); + } elseif ($uri == '/chunk') { + chunk($req, $resp); } else { - no_chunk($req, $resp); + no_chunk($req, $resp); } }); -$http->on('finish', function () -{ +$http->on('finish', function () { echo "task finish"; }); -$http->on('task', function () -{ +$http->on('task', function () { echo "async task\n"; }); @@ -207,8 +206,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) //}); -$http->on('workerStart', function ($serv, $id) -{ +$http->on('workerStart', function ($serv, $id) { //var_dump($serv); }); diff --git a/examples/http/static_handler.php b/examples/http/static_handler.php index 9f8be7f616a..e135f1685fd 100644 --- a/examples/http/static_handler.php +++ b/examples/http/static_handler.php @@ -1,6 +1,6 @@ set([ 'enable_static_handler' => true, 'http_autoindex' => true, diff --git a/examples/http/upload_file.php b/examples/http/upload_file.php deleted file mode 100644 index d5bab206c5e..00000000000 --- a/examples/http/upload_file.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['User-Agent' => "swoole"]); -$cli->addFile(__DIR__.'/post.data', 'post'); -$cli->addFile(dirname(__DIR__).'/test.jpg', 'debug'); - -$cli->post('/dump2.php', array("xxx" => 'abc', 'x2' => 'rango'), function ($cli) { - echo $cli->body; - $cli->close(); -}); diff --git a/examples/http2/server.php b/examples/http2/server.php index fb8c3913054..781ae316a97 100644 --- a/examples/http2/server.php +++ b/examples/http2/server.php @@ -4,7 +4,7 @@ 'log_level' => 0, ]); $key_dir = __DIR__ . '/../ssl/'; -$http = new swoole_http_server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'open_http2_protocol' => 1, 'enable_static_handler' => TRUE, @@ -13,7 +13,7 @@ 'ssl_key_file' => $key_dir . '/ssl.key', ]); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) { +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Test-Value', [ "a\r\n", 'd5678', diff --git a/examples/http2/streaming.php b/examples/http2/streaming.php new file mode 100644 index 00000000000..f0379f80cf4 --- /dev/null +++ b/examples/http2/streaming.php @@ -0,0 +1,21 @@ +set([ + 'open_http2_protocol' => 1, +]); + +/** + * nghttp -v http://localhost:9501 + */ +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n
\n"); + Co\System::sleep(1); + } + $response->end("hello world"); +}); + +$http->start(); + diff --git a/examples/ipv6/tcp_client.php b/examples/ipv6/tcp_client.php index aae16a87dad..b757f5caa83 100644 --- a/examples/ipv6/tcp_client.php +++ b/examples/ipv6/tcp_client.php @@ -1,5 +1,5 @@ connect('::1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/ipv6/tcp_server.php b/examples/ipv6/tcp_server.php index e428476fdf9..80d4b0d374c 100644 --- a/examples/ipv6/tcp_server.php +++ b/examples/ipv6/tcp_server.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/ipv6/udp_client.php b/examples/ipv6/udp_client.php index 1d99d7a1231..a1033cdb24a 100644 --- a/examples/ipv6/udp_client.php +++ b/examples/ipv6/udp_client.php @@ -1,5 +1,5 @@ connect('::1', 9502); $client->send("admin"); echo $client->recv()."\n"; diff --git a/examples/ipv6/udp_server.php b/examples/ipv6/udp_server.php index d312cf0c356..725fc26b687 100644 --- a/examples/ipv6/udp_server.php +++ b/examples/ipv6/udp_server.php @@ -1,9 +1,9 @@ set(array( 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd, $reactor_id)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/length/async_client.php b/examples/length/async_client.php deleted file mode 100644 index 379267b1729..00000000000 --- a/examples/length/async_client.php +++ /dev/null @@ -1,49 +0,0 @@ - str_repeat('A', rand(100000, 900000)), - 'str2' => str_repeat('B', rand(100000, 900000)), - 'str3' => str_repeat('C', rand(10000, 90000)), - ); - - $data['int1'] = rand(100000, 999999); - - $sendStr = serialize($data); - $sendData = pack('N', strlen($sendStr)) . $sendStr; - $cli->send($sendData); - echo "send length=" . strlen($sendData) . ", SerId={$data['int1']}\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 - -$client->set(array( - 'open_length_check' => 1, - 'package_length_type' => 'N', - 'package_length_offset' => 0, //第N个字节是包长度的值 - 'package_body_offset' => 4, //第几个字节开始计算长度 - 'package_max_length' => 2000000, //协议最大长度 -)); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - $resp = unserialize(substr($data, 4)); - echo "recv length=" . strlen($data) . ", SerId={$resp['int1']}\n".str_repeat('-', 60)."\n"; - $cli->close(); -// sleep(1); - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/length/client.php b/examples/length/client.php index bba05fe4f7a..2a6c7f38ed0 100644 --- a/examples/length/client.php +++ b/examples/length/client.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, diff --git a/examples/length/func.php b/examples/length/func.php index f37ebd7efef..2846cbeb34c 100644 --- a/examples/length/func.php +++ b/examples/length/func.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -17,7 +17,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/length/server.php b/examples/length/server.php index 420db2c8ba6..2f5b21d4ead 100644 --- a/examples/length/server.php +++ b/examples/length/server.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -11,7 +11,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -function send(swoole_server $serv, $fd, $data) +function send(Swoole\Server $serv, $fd, $data) { $serv->send($fd, $data); echo "#send =" . strlen($data) . " bytes\n"; @@ -21,7 +21,7 @@ function send(swoole_server $serv, $fd, $data) echo "Client:Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $req = unserialize(substr($data, 4)); echo "#{$serv->worker_id}>> received length=" . strlen($data) . ", SerId: {$req['int1']}\n"; send($serv, $fd, $data); diff --git a/examples/lock/lock.php b/examples/lock/lock.php index 6a699d7dd76..3254889512e 100644 --- a/examples/lock/lock.php +++ b/examples/lock/lock.php @@ -7,7 +7,7 @@ * SWOOLE_RWLOCK 读写锁 */ -$lock = new swoole_lock(SWOOLE_MUTEX); +$lock = new Swoole\Lock(SWOOLE_MUTEX); echo "[Master]create lock\n"; $lock->lock(); if (pcntl_fork() > 0) diff --git a/examples/logo.svg b/examples/logo.svg deleted file mode 100644 index 3e06c3fdc4f..00000000000 --- a/examples/logo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/examples/get_local_ip.php b/examples/misc/get_local_ip.php similarity index 100% rename from examples/get_local_ip.php rename to examples/misc/get_local_ip.php diff --git a/examples/version.php b/examples/misc/version.php similarity index 100% rename from examples/version.php rename to examples/misc/version.php diff --git a/examples/multi_port_server.php b/examples/multi_port_server.php deleted file mode 100644 index eaaa4d4c5f2..00000000000 --- a/examples/multi_port_server.php +++ /dev/null @@ -1,44 +0,0 @@ -addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); - -$port->on('packet', function($serv, $data, $client) { - var_dump($data, $client); - $serv->sendto($client['address'], $client['port'], "welcome admin\n"); -}); -$serv->on('connect', function ($serv, $fd) { - echo "Client:Connect.\n"; -}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - $info = $serv->connection_info($fd, $reactor_id); - //来自9502的内网管理端口 - if($info['server_port'] == 9502) { - $serv->send($fd, "welcome admin\n"); - $start_fd = 0; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - var_dump($conn_list); - - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $serv->send($conn, "hello from $fd\n"); - } - } - } - //来自外网 - else { - $serv->send($fd, 'Swoole: '.$data); - } -}); -$serv->on('close', function ($serv, $fd) { - echo "Client: Close.\n"; -}); -$serv->start(); diff --git a/examples/multicast/client.php b/examples/multicast/client.php index bbee8016948..91a46c5cf63 100644 --- a/examples/multicast/client.php +++ b/examples/multicast/client.php @@ -1,5 +1,5 @@ connect('224.10.20.30', 9905); $client->send("hello world"); echo $client->recv() . "\n"; diff --git a/examples/multicast/server.php b/examples/multicast/server.php index 20b68d92ee9..7e6655032fa 100644 --- a/examples/multicast/server.php +++ b/examples/multicast/server.php @@ -1,5 +1,5 @@ set(['worker_num' => 1]); $socket = $server->getSocket(); @@ -10,15 +10,13 @@ array('group' => '224.10.20.30', 'interface' => 0) ); -if ($ret === false) -{ +if ($ret === false) { throw new RuntimeException('Unable to join multicast group'); } -$server->on('Packet', function (swoole_server $serv, $data, $addr) -{ +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data"); - var_dump( $addr, strlen($data)); + var_dump($addr, strlen($data)); }); $server->start(); diff --git a/examples/mysql_proxy_server.php b/examples/mysql_proxy_server.php deleted file mode 100644 index 0471b0705f9..00000000000 --- a/examples/mysql_proxy_server.php +++ /dev/null @@ -1,126 +0,0 @@ -set(array( - 'worker_num' => 1, - 'max_request' => 0, - )); - - $serv->on('WorkerStart', array($this, 'onStart')); - //$serv->on('Connect', array($this, 'onConnect')); - $serv->on('Receive', array($this, 'onReceive')); - //$serv->on('Close', array($this, 'onClose')); - $serv->start(); - } - - function onStart($serv) - { - $this->serv = $serv; - for ($i = 0; $i < $this->pool_size; $i++) { - $db = new mysqli; - $db->connect('127.0.0.1', 'root', 'root', 'www4swoole'); - $db_sock = swoole_get_mysqli_sock($db); - swoole_event_add($db_sock, array($this, 'onSQLReady')); - $this->idle_pool[] = array( - 'mysqli' => $db, - 'db_sock' => $db_sock, - 'fd' => 0, - ); - } - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; - } - - function onSQLReady($db_sock) - { - $db_res = $this->busy_pool[$db_sock]; - $mysqli = $db_res['mysqli']; - $fd = $db_res['fd']; - - echo __METHOD__ . ": client_sock=$fd|db_sock=$db_sock\n"; - - if ($result = $mysqli->reap_async_query()) { - $ret = var_export($result->fetch_all(MYSQLI_ASSOC), true) . "\n"; - $this->serv->send($fd, $ret); - if (is_object($result)) { - mysqli_free_result($result); - } - } else { - $this->serv->send($fd, sprintf("MySQLi Error: %s\n", mysqli_error($mysqli))); - } - //release mysqli object - $this->idle_pool[] = $db_res; - unset($this->busy_pool[$db_sock]); - - //这里可以取出一个等待请求 - if (count($this->wait_queue) > 0) { - $idle_n = count($this->idle_pool); - for ($i = 0; $i < $idle_n; $i++) { - $req = array_shift($this->wait_queue); - $this->doQuery($req['fd'], $req['sql']); - } - } - } - - function onReceive($serv, $fd, $reactor_id, $data) - { - echo "Received: $data\n"; - //没有空闲的数据库连接 - - if (count($this->idle_pool) == 0) { - //等待队列未满 - if (count($this->wait_queue) < $this->wait_queue_max) { - $this->wait_queue[] = array( - 'fd' => $fd, - 'sql' => $data, - ); - } else { - $this->serv->send($fd, "request too many, Please try again later."); - } - } else { - $this->doQuery($fd, $data); - } - } - - function doQuery($fd, $sql) - { - //从空闲池中移除 - $db = array_pop($this->idle_pool); - /** - * @var mysqli - */ - $mysqli = $db['mysqli']; - - for ($i = 0; $i < 2; $i++) { - $result = $mysqli->query($sql, MYSQLI_ASYNC); - if ($result === false) { - if ($mysqli->errno == 2013 or $mysqli->errno == 2006) { - $mysqli->close(); - $r = $mysqli->connect(); - if ($r === true) continue; - } - } - break; - } - - $db['fd'] = $fd; - //加入工作池中 - $this->busy_pool[$db['db_sock']] = $db; - } -} - -$server = new DBServer(); -$server->run(); diff --git a/examples/namespace/README.md b/examples/namespace/README.md deleted file mode 100644 index a396a3f5c5c..00000000000 --- a/examples/namespace/README.md +++ /dev/null @@ -1,24 +0,0 @@ -Enable Namespace Class ---------- -modify your `php.ini` file. - -```shell -swoole.use_namespace = on -``` - - -```php -use Swoole\Http\Server; -use Swoole\Http\Request; -use Swoole\Http\Response; - -$serv = new Server('127.0.0.1', 9501); - -$serv->on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); - -``` diff --git a/examples/namespace/atomic.php b/examples/namespace/atomic.php deleted file mode 100644 index 523e333f2d3..00000000000 --- a/examples/namespace/atomic.php +++ /dev/null @@ -1,5 +0,0 @@ -add(12); -echo $an->get()."\n"; diff --git a/examples/namespace/http_server.php b/examples/namespace/http_server.php deleted file mode 100644 index 6560089186b..00000000000 --- a/examples/namespace/http_server.php +++ /dev/null @@ -1,13 +0,0 @@ -on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); diff --git a/examples/namespace/server.php b/examples/namespace/server.php deleted file mode 100644 index 661751d5f08..00000000000 --- a/examples/namespace/server.php +++ /dev/null @@ -1,14 +0,0 @@ -on('receive', function (Server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; - if ($serv->send($fd, "hello\n") == false) - { - echo "error\n"; - } -}); - -$serv->start(); diff --git a/examples/namespace/timer.php b/examples/namespace/timer.php deleted file mode 100644 index e9f551745c2..00000000000 --- a/examples/namespace/timer.php +++ /dev/null @@ -1,4 +0,0 @@ -connect('127.0.0.1', 'root', 'root', 'test'); - -$db->query("show databases", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); - -$db->query("show tables", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); diff --git a/examples/process/alarm.php b/examples/process/alarm.php index 859d1ab0d81..32d85db7f75 100644 --- a/examples/process/alarm.php +++ b/examples/process/alarm.php @@ -1,13 +1,13 @@ 20) { - swoole_process::alarm(-1); + Swoole\Process::alarm(-1); } }); -swoole_process::alarm(100 * 1000); +Swoole\Process::alarm(100 * 1000); diff --git a/examples/process/async_master.php b/examples/process/async_master.php index c6b8ae8c8e7..14b63d0ab24 100644 --- a/examples/process/async_master.php +++ b/examples/process/async_master.php @@ -2,7 +2,7 @@ $workers = []; $worker_num = 10; -//swoole_process::daemon(0, 1); +//Swoole\Process::daemon(0, 1); function onReceive($pipe) { global $workers; @@ -14,7 +14,7 @@ function onReceive($pipe) { //循环创建进程 for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process(function(swoole_process $process) { + $process = new Swoole\Process(function(Swoole\Process $process) { $i = 1; while($i++) { @@ -28,14 +28,14 @@ function onReceive($pipe) { $workers[$process->pipe] = $process; } -swoole_process::signal(SIGCHLD, function(){ +Swoole\Process::signal(SIGCHLD, function(){ //表示子进程已关闭,回收它 - $status = swoole_process::wait(); + $status = Swoole\Process::wait(); echo "Worker#{$status['pid']} exit\n"; }); //将子进程的管道加入EventLoop foreach($workers as $process) { - swoole_event_add($process->pipe, 'onReceive'); + Swoole\Event::add($process->pipe, 'onReceive'); } diff --git a/examples/process/client.php b/examples/process/client.php index 2dd195e2a37..181792038a4 100644 --- a/examples/process/client.php +++ b/examples/process/client.php @@ -1,12 +1,12 @@ connect('127.0.0.1', 8089, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } -function _send(swoole_client $client, $data) +function _send(Swoole\Client $client, $data) { return $client->send(pack('N', strlen($data)) . $data); } diff --git a/examples/process/close.php b/examples/process/close.php index 5b00c27fd32..df52e7f5d04 100644 --- a/examples/process/close.php +++ b/examples/process/close.php @@ -1,9 +1,9 @@ pid . "\n"; sleep(2); - $worker->close(swoole_process::PIPE_READ); + $worker->close(Swoole\Process::PIPE_READ); $worker->write("hello master\n"); $worker->exit(0); }, false); diff --git a/examples/process/exec.php b/examples/process/exec.php index 7c7fd95bd9e..5236d423359 100644 --- a/examples/process/exec.php +++ b/examples/process/exec.php @@ -1,8 +1,8 @@ start(); -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { $worker->exec('/usr/local/bin/php', array(__DIR__.'/stdin_stdout.php')); } @@ -11,5 +11,5 @@ function callback_function(swoole_process $worker) $process->write("hello worker\n"); echo "From Worker: ".$process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/msgqueue.php b/examples/process/msgqueue.php index f88c45b0399..03a5b0ccfd9 100644 --- a/examples/process/msgqueue.php +++ b/examples/process/msgqueue.php @@ -4,14 +4,14 @@ for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process('callback_function', false, false); + $process = new Swoole\Process('callback_function', false, false); $process->useQueue(); $pid = $process->start(); $workers[$pid] = $process; //echo "Master: new worker, PID=".$pid."\n"; } -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -28,7 +28,7 @@ function callback_function(swoole_process $worker) while(true) { /** - * @var $process swoole_process + * @var $process Swoole\Process */ $pid = array_rand($workers); $process = $workers[$pid]; @@ -38,7 +38,7 @@ function callback_function(swoole_process $worker) for($i = 0; $i < $worker_num; $i++) { - $ret = swoole_process::wait(); + $ret = Swoole\Process::wait(); $pid = $ret['pid']; unset($workers[$pid]); echo "Worker Exit, PID=".$pid.PHP_EOL; diff --git a/examples/process/msgqueue2.php b/examples/process/msgqueue2.php index b3d7172867b..dfa53c2920b 100644 --- a/examples/process/msgqueue2.php +++ b/examples/process/msgqueue2.php @@ -1,5 +1,5 @@ pid."\n"; //recv data from master @@ -13,8 +13,8 @@ function callback_function(swoole_process $worker) $worker->exit(0); } -$process = new swoole_process('callback_function', false, false); -$process->useQueue(ftok(__FILE__, 1), 2 | swoole_process::IPC_NOWAIT); +$process = new Swoole\Process('callback_function', false, false); +$process->useQueue(ftok(__FILE__, 1), 2 | Swoole\Process::IPC_NOWAIT); $send_bytes = 0; foreach(range(1, 10) as $i) diff --git a/examples/process/python.php b/examples/process/python.php index b66450b010c..8efe55f4fba 100644 --- a/examples/process/python.php +++ b/examples/process/python.php @@ -1,8 +1,8 @@ start(); -function pyhon_process(swoole_process $worker) +function pyhon_process(Swoole\Process $worker) { $worker->exec('/usr/bin/python', array("echo.py")); } @@ -10,5 +10,5 @@ function pyhon_process(swoole_process $worker) $process->write("hello world\n"); echo $process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/select.php b/examples/process/select.php index 60174f33d13..2b2745c5a39 100644 --- a/examples/process/select.php +++ b/examples/process/select.php @@ -1,5 +1,5 @@ pid . "\n"; sleep(2); diff --git a/examples/process/set_cpu_affinity.php b/examples/process/set_cpu_affinity.php new file mode 100644 index 00000000000..f454327f2e9 --- /dev/null +++ b/examples/process/set_cpu_affinity.php @@ -0,0 +1,3 @@ +id = $i; $pid = $process->start(); $workers[$pid] = $process; @@ -19,10 +19,10 @@ //异步主进程 function master_async($workers) { - swoole_process::signal(SIGCHLD, function ($signo) use (&$workers) { + Swoole\Process::signal(SIGCHLD, function ($signo) use (&$workers) { while(1) { - $ret = swoole_process::wait(false); + $ret = Swoole\Process::wait(false); if ($ret) { $pid = $ret['pid']; @@ -41,11 +41,11 @@ function master_async($workers) }); /** - * @var $process swoole_process + * @var $process Swoole\Process */ foreach($workers as $pid => $process) { - swoole_event_add($process->pipe, function($pipe) use ($process) { + Swoole\Event::add($process->pipe, function($pipe) use ($process) { $recv = $process->read(); if ($recv) echo "From Worker: " . $recv; $process->write("HELLO worker {$process->pid}\n"); @@ -64,7 +64,7 @@ function master_sync($workers) } } -function child_sync(swoole_process $worker) +function child_sync(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -79,7 +79,7 @@ function child_sync(swoole_process $worker) $worker->exit(0); } -function child_async(swoole_process $worker) +function child_async(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -87,18 +87,18 @@ function child_async(swoole_process $worker) global $argv; $worker->name("{$argv[0]}: worker #".$worker->id); - swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { + Swoole\Process::signal(SIGTERM, function($signal_num) use ($worker) { echo "signal call = $signal_num, #{$worker->pid}\n"; }); -// swoole_timer_tick(2000, function () use ($worker) +// Swoole\Timer::tick(2000, function () use ($worker) // { // if (rand(1, 3) % 2) { // $worker->write("hello master {$worker->pid}\n"); // } // }); - swoole_event_add($worker->pipe, function($pipe) use($worker) { + Swoole\Event::add($worker->pipe, function($pipe) use($worker) { $recv = $worker->read(); echo "From Master: $recv\n"; //$worker->write("hello master\n"); diff --git a/examples/recv_1m_client.php b/examples/recv_1m_client.php deleted file mode 100644 index 9d8a960d82d..00000000000 --- a/examples/recv_1m_client.php +++ /dev/null @@ -1,27 +0,0 @@ -connect('127.0.0.1', 9509, 60); -$c->send("AAAAAAAAAAAAAAAA"); - -$n_bytes = 0; - -while (true) -{ - $line = $c->recv(); - if ($line === false) - { - echo "recv failed.\n"; - break; - } - elseif (empty($line)) - { - echo "recv $n_bytes bytes\n"; - break; - } - else - { - fwrite($f, $line); - $n_bytes += strlen($line); - } -} diff --git a/examples/recv_file.php b/examples/recv_file.php deleted file mode 100644 index 717ba2a4e50..00000000000 --- a/examples/recv_file.php +++ /dev/null @@ -1,45 +0,0 @@ -connect($server_ip, 9501, 5); -$filesize = intval($cli->recv()); -if ($filesize == 0) -{ - die("get file size failed.\n"); -} -echo "file_size = $filesize\n"; -$content = ''; -$cli->send("get file"); - -$use_waitall = false; - -if ($use_waitall) -{ - //waitall,需要一次性分配内存,适合小一点的文件 - $content = $cli->recv($filesize, true); -} -else -{ - //循环接收,适合大型文件 - while(1) - { - //超大文件接收,这里需要改成分段写磁盘 - $content .= $cli->recv(); - if (strlen($content) == $filesize) - { - break; - } - } -} -file_put_contents(__DIR__."/recv_file_".time().".jpg", $content); -echo "recv ".strlen($content)." byte data\n"; -echo "used ".((microtime(true) - $start_ms)*1000)."ms\n"; -$cli->close(); diff --git a/examples/reflection_test.php b/examples/reflection_test.php deleted file mode 100644 index 5a1c5cebd66..00000000000 --- a/examples/reflection_test.php +++ /dev/null @@ -1,12 +0,0 @@ -getMethods(); -foreach($methods as $method) { - echo "----------------------------------------" .PHP_EOL; - echo "method name : " . $method->name . PHP_EOL; - echo "----------------------------------------" . PHP_EOL; - $method = $Ref_swoole_server->getMethod($method->name); - $params = $method->getParameters(); - print_r($params); -} diff --git a/examples/runtime/curl.php b/examples/runtime/curl.php index 2d586e270a0..b6c250231e8 100644 --- a/examples/runtime/curl.php +++ b/examples/runtime/curl.php @@ -16,4 +16,4 @@ echo strlen($output) . "bytes\n"; }); -swoole_event_wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/runtime/file.php b/examples/runtime/file.php index 84eadc606df..317735d69cb 100644 --- a/examples/runtime/file.php +++ b/examples/runtime/file.php @@ -1,7 +1,7 @@ exec('create table test (id int)'); + $dbh->exec('insert into test values(1)'); + $dbh->exec('insert into test values(2)'); + $res = $dbh->query("select * from test"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]); + +Co\run(function () { + test(); +}); diff --git a/examples/runtime/odbc.php b/examples/runtime/odbc.php new file mode 100644 index 00000000000..678c2e25bbe --- /dev/null +++ b/examples/runtime/odbc.php @@ -0,0 +1,22 @@ +query("select sleep(1) s"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['trace_flags' => SWOOLE_TRACE_CO_ODBC, 'log_level' => SWOOLE_LOG_DEBUG]); + +Co\run(function () { + test(); +}); diff --git a/examples/runtime/select.php b/examples/runtime/select.php index 45f7367bf91..d73353dad02 100644 --- a/examples/runtime/select.php +++ b/examples/runtime/select.php @@ -21,4 +21,4 @@ } }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/runtime/sqlite.php b/examples/runtime/sqlite.php new file mode 100644 index 00000000000..7f31bd71105 --- /dev/null +++ b/examples/runtime/sqlite.php @@ -0,0 +1,15 @@ + SWOOLE_HOOK_PDO_SQLITE]); + +run(function() { + $db = new PDO('sqlite::memory:'); + for ($i = 0; $i < 10; $i++) { + go(function() use($i, $db) { + $db->query('select randomblob(99999999)'); + var_dump($i); + }); + } +}); diff --git a/examples/send_1m_svr.php b/examples/send_1m_svr.php deleted file mode 100644 index 00d1680899d..00000000000 --- a/examples/send_1m_svr.php +++ /dev/null @@ -1,30 +0,0 @@ -set(array('worker_num' => 1)); -$serv->on('workerStart', function($serv, $worker_id) { - //if($worker_id == 0) $serv->addtimer(500); -}); -$serv->on('connect', function ($serv, $fd, $reactor_id){ - $serv->array['fd'] = &strval($fd); - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; -}); -$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { - //echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; - $array = array('A', 'B', 'C', 'D', 'E', 'F', 'G'); - $data = ''; - $n_bytes = 0; - for ($i = 0; $i < 10; $i++) - { - $_str = str_repeat($array[$i % 7], 4030) . "\n"; - //$serv->send($fd, $_str); - $n_bytes += strlen($_str); - $data .= $_str; - } - echo "send " . $n_bytes . " bytes\n"; - $serv->send( $serv->array['fd'], $data); - $serv->close($fd); -}); -$serv->on('close', function ($serv, $fd, $reactor_id) { - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; -}); -$serv->start(); diff --git a/examples/sendfile_server.php b/examples/sendfile_server.php deleted file mode 100644 index c5ccdaf2d1b..00000000000 --- a/examples/sendfile_server.php +++ /dev/null @@ -1,24 +0,0 @@ -set(array( - 'worker_num' => 1, -)); -$serv->on('timer', function($serv, $interval) { - echo "onTimer: $interval\n"; -}); -$serv->on('workerStart', function($serv, $worker_id) { - //if($worker_id == 0) $serv->addtimer(300); -}); -$serv->on('connect', function (swoole_server $serv, $fd){ - $serv->send($fd, filesize(__DIR__.'/test.jpg')); - //echo "Client:Connect.\n"; -}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - echo "Client[$fd]: $data\n"; - $serv->sendfile($fd, __DIR__.'/test.jpg'); - //$serv->close($fd); -}); -$serv->on('close', function ($serv, $fd) { - //echo "Client: Close.\n"; -}); -$serv->start(); diff --git a/examples/serialize.php b/examples/serialize.php deleted file mode 100644 index b3e067a0cd3..00000000000 --- a/examples/serialize.php +++ /dev/null @@ -1,17 +0,0 @@ -pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); - -?> diff --git a/examples/serialize2.php b/examples/serialize2.php deleted file mode 100644 index 3ee95c247d2..00000000000 --- a/examples/serialize2.php +++ /dev/null @@ -1,30 +0,0 @@ -sub = new mySubObject(); -$arr->sub->default = new stdclass(); -$obj = new \Swoole\Serialize(); -$ser = $obj->pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); -var_dump($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump($obj->unpack($ser2, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump(UNSERIALIZE_OBJECT_TO_ARRAY); -var_dump(UNSERIALIZE_OBJECT_TO_STDCLASS); -var_dump(get_class($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS))); - -?> diff --git a/examples/server.php b/examples/server.php deleted file mode 100644 index 49c2ad48af4..00000000000 --- a/examples/server.php +++ /dev/null @@ -1,537 +0,0 @@ - 16, // 线程数. 一般设置为CPU核数的1-4倍 - 'worker_num' => 2, // 工作进程数量. 设置为CPU的1-4倍最合理 - 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) - 'max_conn' => 10000, // 最大连接数 - 'task_worker_num' => 1, // 任务工作进程数量 -// 'task_ipc_mode' => 2, // 设置 Task 进程与 Worker 进程之间通信的方式。 - 'task_max_request' => 0, // 防止 PHP 内存溢出 - //'task_tmpdir' => '/tmp', - //'message_queue_key' => ftok(SYS_ROOT . 'queue.msg', 1), - 'dispatch_mode' => 2, - //'daemonize' => 1, // 设置守护进程模式 - 'backlog' => 128, - //'log_file' => '/data/logs/swoole.log', - 'heartbeat_check_interval' => 2, // 心跳检测间隔时长(秒) - 'heartbeat_idle_time' => 3, // 连接最大允许空闲的时间 - //'open_eof_check' => 1, - //'open_eof_split' => 1, - //'package_eof' => "\r\r\n", - //'open_cpu_affinity' => 1, - 'socket_buffer_size' => 1024 * 1024 * 128, - 'output_buffer_size' => 1024 * 1024 * 2, - //'enable_delay_receive' => true, - //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 - ); -} - -if (isset($argv[1]) and $argv[1] == 'daemon') { - G::$config['daemonize'] = true; -} else { - G::$config['daemonize'] = false; -} - -//$mode = SWOOLE_BASE; -$mode = SWOOLE_PROCESS; - -$serv = new swoole_server("0.0.0.0", 9501, $mode, SWOOLE_SOCK_TCP); -$serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); -$serv->listen('::', 9503, SWOOLE_SOCK_TCP6); -$serv->listen('::', 9504, SWOOLE_SOCK_UDP6); -$process1 = new swoole_process(function ($worker) use ($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: my_process1"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { - echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx - foreach ($serv->connections as $conn) - { - $serv->send($conn, "heartbeat\n"); - } - }); - swoole_timer_tick(5000, function () use ($serv) - { - $serv->sendMessage("hello event worker", 0); - $serv->sendMessage("hello task worker", 4); - }); -}, false); - -//$serv->addprocess($process1); - -$process2 = new swoole_process(function ($worker) use ($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: my_process2"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { - echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx - }); -}, false); - -//$serv->addprocess($process2); - -$serv->set(G::$config); -$serv->set(['reactor_num' => 4]); - -/** - * 使用类的静态属性,可以直接访问 - */ -G::$serv = $serv; - -function my_onStart(swoole_server $serv) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: master"); - my_log("Server: start.Swoole version is [".SWOOLE_VERSION."]"); - my_log("MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}"); -} - -function my_log($msg) -{ - global $serv; - if (empty($serv->worker_pid)) - { - $serv->worker_pid = posix_getpid(); - } - echo "#".$serv->worker_pid."\t[".date('H:i:s')."]\t".$msg.PHP_EOL; -} - -function forkChildInWorker() { - global $serv; - echo "on worker start\n"; - $process = new swoole_process( function (swoole_process $worker) use ($serv) { -// $serv = new swoole_server( "0.0.0.0", 9503 ); -// $serv->set(array( -// 'worker_num' => 1 -// )); -// $serv->on ( 'receive', function (swoole_server $serv, $fd, $reactor_id, $data) { -// $serv->send ( $fd, "Swoole: " . $data ); -// $serv->close ( $fd ); -// }); -// $serv->start (); -// swoole_event_add ($worker->pipe, function ($pipe) use ($worker) { -// echo $worker->read()."\n"; -// }); - }); - - $pid = $process->start(); - echo "Fork child process success. pid={$pid}\n"; - //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 - $serv->childprocess = $process; -} - -function processRename(swoole_server $serv, $worker_id) { - - global $argv; - if ( $serv->taskworker) - { - swoole_set_process_name("php {$argv[0]}: task"); - } - else - { - swoole_set_process_name("php {$argv[0]}: worker"); - } -// if ($worker_id == 0) -// { -// var_dump($serv->setting); -// } - my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); -} - -function setTimerInWorker(swoole_server $serv, $worker_id) { - - if ($worker_id == 0) { - echo "Start: ".microtime(true)."\n"; - //$serv->addtimer(3000); -// $serv->addtimer(7000); - //var_dump($serv->gettimer()); - } -// $serv->after(2000, function(){ -// echo "Timeout: ".microtime(true)."\n"; -// }); -// $serv->after(5000, function(){ -// echo "Timeout: ".microtime(true)."\n"; -// global $serv; -// $serv->deltimer(3000); -// }); -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onClose(swoole_server $serv, $fd, $reactor_id) -{ - my_log("Client[$fd@$reactor_id]: fd=$fd is closed"); - var_dump($serv->getClientInfo($fd)); -} - -function my_onConnect(swoole_server $serv, $fd, $reactor_id) -{ - //throw new Exception("hello world"); -// var_dump($serv->connection_info($fd)); - //var_dump($serv, $fd, $reactor_id); -// echo "Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\n"; - $serv->after(2000, function() use ($serv, $fd) { - $serv->confirm($fd); - }); - my_log("Client: Connect --- {$fd}"); -} - -function timer_show($id) -{ - my_log("Timer#$id"); -} - -function my_onWorkerExit(swoole_server $serv, $worker_id) { - global $argv; -} - -function my_onWorkerStart(swoole_server $serv, $worker_id) -{ - processRename($serv, $worker_id); - - if (!$serv->taskworker) - { - swoole_process::signal(SIGUSR2, function($signo){ - echo "SIGNAL: $signo\n"; - }); - $serv->defer(function(){ - echo "defer call\n"; - }); -// $serv->tick(2000, function() use ($serv) { -// echo "Worker-{$serv->worker_id} tick-2000\n"; -// }); - } - else - { -// swoole_timer_after(2000, function() { -// echo "after 2 secends.\n"; -// }); -// $serv->tick(1000, function ($id) use ($serv) { -// if (G::$index > 10) { -// $serv->after(2500, 'timer_show', 2); -// G::$index = 0; -// } else { -// G::$index++; -// } -// timer_show($id); -// }); - } - //forkChildInWorker(); -// setTimerInWorker($serv, $worker_id); -} - -function my_onWorkerStop($serv, $worker_id) -{ - echo "WorkerStop[$worker_id]|pid=".$serv->worker_pid.".\n"; -} - -function my_onPacket($serv, $data, $clientInfo) -{ - $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data); - var_dump($clientInfo); -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); - $cmd = trim($data); - if($cmd == "reload") - { - $serv->reload(); - } - elseif($cmd == "task") - { - $task_id = $serv->task("task ".$fd); - echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif ($cmd == "taskclose") - { - $serv->task("close " . $fd); - echo "close the connection in taskworker\n"; - } - elseif ($cmd == "tasksend") - { - $serv->task("send " . $fd); - } - elseif ($cmd == "bigtask") - { - $serv->task(str_repeat('A', 8192*5)); - } - elseif($cmd == "taskwait") - { - $result = $serv->taskwait("taskwait"); - if ($result) { - $serv->send($fd, "taskwaitok"); - } - echo "SyncTask: result=".var_export($result, true)."\n"; - } - elseif($cmd == "taskWaitMulti") - { - $result = $serv->taskWaitMulti(array( - str_repeat('A', 8192 * 5), - str_repeat('B', 8192 * 6), - str_repeat('C', 8192 * 8) - )); - if ($result) - { - $resp = "taskWaitMulti ok\n"; - foreach($result as $k => $v) - { - $resp .= "result[$k] length=".strlen($v)."\n"; - } - $serv->send($fd, $resp); - } - else - { - $serv->send($fd, "taskWaitMulti error\n"); - } - } - elseif ($cmd == "hellotask") - { - $serv->task("hellotask"); - } - elseif ($cmd == "taskcallback") - { - $serv->task("taskcallback", -1, function (swoole_server $serv, $task_id, $data) - { - echo "Task Callback: "; - var_dump($task_id, $data); - }); - } - elseif ($cmd == "sendto") - { - $serv->sendto("127.0.0.1", 9999, "hello world"); - } - elseif($cmd == "close") - { - $serv->send($fd, "close connection\n"); - $result = $serv->close($fd); - } - elseif($cmd == "info") - { - $info = $serv->connection_info(strval($fd), $reactor_id); - var_dump($info["remote_ip"]); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif ($cmd == 'proxy') - { - $serv->send(1, "hello world\n"); - } - elseif ($cmd == 'sleep') - { - sleep(10); - } - elseif ($cmd == 'foreach') - { - foreach($serv->connections as $fd) - { - echo "conn : $fd\n"; - } - return; - } - elseif ($cmd == 'tick') - { - $serv->tick(2000, function ($id) { - echo "tick #$id\n"; - }); - } - elseif ($cmd == 'addtimer') - { - $serv->addtimer(3000); - } - elseif($cmd == "list") - { - $start_fd = 0; - echo "broadcast\n"; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if (empty($conn_list)) - { - echo "iterates finished\n"; - break; - } - $start_fd = end($conn_list); - var_dump($conn_list); - } - } - elseif($cmd == "list2") - { - foreach($serv->connections as $con) - { - var_dump($serv->connection_info($con)); - } - } - elseif($cmd == "stats") - { - $serv_stats = $serv->stats(); - $serv->send($fd, 'Stats: '.var_export($serv_stats, true)."\ncount=".count($serv->connections).PHP_EOL); - } - elseif($cmd == "broadcast") - { - broadcast($serv, $fd, "hello from $fd\n"); - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { - hello_no_exists(); - } - elseif($cmd == "exit") - { - exit("worker php exit.\n"); - } - //关闭fd - elseif(substr($cmd, 0, 5) == "close") - { - $close_fd = substr($cmd, 6); - $serv->close($close_fd); - } - elseif($cmd == "shutdown") - { - $serv->shutdown(); - } - elseif($cmd == "fatalerror") - { - require __DIR__.'/php/error.php'; - } - elseif($cmd == 'defer') - { - $serv->defer(function() use ($fd, $serv) { - $serv->close($fd); - $serv->defer(function(){ - echo "deferd\n"; - }); - }); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - } - else - { - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - //$serv->close($fd); - } - //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; -// $serv->after( -// 800, function () { -// echo "hello"; -// } -// ); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - if ($data == 'taskwait') - { - $fd = str_replace('task-', '', $data); - $serv->send($fd, "hello world"); - return array("task" => 'wait'); - } - elseif ($data == 'taskcallback') - { - return array("task" => 'callback'); - } - else - { - $cmd = explode(' ', $data); - if ($cmd[0] == 'send') - { - $serv->send($cmd[1], str_repeat('A', 10000)."\n"); - } - elseif ($cmd[0] == 'close') - { - $serv->close($cmd[1]); - } - else - { - echo "bigtask: length=".strlen($data)."\n"; - return $data; - } -// $serv->sendto('127.0.0.1', 9999, "hello world"); - //swoole_timer_after(1000, "test"); -// var_dump($data); -// $serv->send($fd, str_repeat('A', 8192 * 2)); -// $serv->send($fd, str_repeat('B', 8192 * 2)); -// $serv->send($fd, str_repeat('C', 8192 * 2)); -// $serv->send($fd, str_repeat('D', 8192 * 2)); - return; - } - - if ($data == "hellotask") - { - broadcast($serv, 0, "hellotask"); - } - else - { - echo "AsyncTask[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; - //eg: test-18 - return $data; - } -} - -function my_onFinish(swoole_server $serv, $task_id, $data) -{ - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'taskok'); - var_dump($str, $fd); - echo "AsyncTask Finish: result={$data}. PID=".$serv->worker_pid.PHP_EOL; -} - -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code, $signo) -{ - echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code|Signal=$signo\n"; -} - -function broadcast(swoole_server $serv, $fd = 0, $data = "hello") -{ - $start_fd = 0; - echo "broadcast\n"; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $ret1 = $serv->send($conn, $data); - //var_dump($ret1); - //$ret2 = $serv->close($conn); - //var_dump($ret2); - } - } -} - -$serv->on('PipeMessage', function($serv, $src_worker_id, $msg) { - my_log("PipeMessage: Src={$src_worker_id},Msg=".trim($msg)); - if ($serv->taskworker) - { - $serv->sendMessage("hello user process", - $src_worker_id); - } -}); - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Packet', 'my_onPacket'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('WorkerStart', 'my_onWorkerStart'); -$serv->on('WorkerStop', 'my_onWorkerStop'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('WorkerError', 'my_onWorkerError'); -$serv->on('WorkerExit', 'my_onWorkerExit'); -$serv->on('ManagerStart', function($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: manager"); -}); -$serv->start(); diff --git a/examples/db_pool.php b/examples/server/db_pool.php similarity index 96% rename from examples/db_pool.php rename to examples/server/db_pool.php index 9878bf34066..c12ede8875c 100644 --- a/examples/db_pool.php +++ b/examples/server/db_pool.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 100, diff --git a/examples/server/dispatch_func.php b/examples/server/dispatch_func.php index 4db4a52843a..df440a0a6b5 100644 --- a/examples/server/dispatch_func.php +++ b/examples/server/dispatch_func.php @@ -1,5 +1,5 @@ set(array( 'dispatch_func' => function ($serv, $fd, $type, $data) { @@ -8,7 +8,7 @@ }, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/dispatch_stream.php b/examples/server/dispatch_stream.php index 536e431b763..8ae0af553fd 100644 --- a/examples/server/dispatch_stream.php +++ b/examples/server/dispatch_stream.php @@ -1,12 +1,12 @@ set(array( 'dispatch_mode' => 7, 'worker_num' => 2, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/echo.php b/examples/server/echo.php index 410f0183388..cdb1fa25729 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,29 +1,44 @@ mode === SWOOLE_THREAD ? \Swoole\Thread::getId() : posix_getpid(); +} $serv->set([ - 'worker_num' =>1, + 'worker_num' => 2, + 'task_worker_num' => 3, ]); -$serv->on('connect', function ($serv, $fd, $reactor_id){ - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; +$serv->on('workerStart', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$serv->on('workerStop', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is stopped.\n"; }); -$serv->set(array( - 'worker_num' => 1, -)); +$serv->on('connect', function ($serv, $fd, $reactor_id) { + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Connect.\n"; +}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { echo "error\n"; } - }); $serv->on('close', function ($serv, $fd, $reactor_id) { - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Close.\n"; +}); + +$serv->on('task', function ($serv, $src_worker_id, $task) { + var_dump($task); }); $serv->start(); diff --git a/examples/server/eof_client.php b/examples/server/eof_client.php index 4ca6200889e..c2768b425f4 100644 --- a/examples/server/eof_client.php +++ b/examples/server/eof_client.php @@ -1,6 +1,6 @@ set( array( 'open_eof_split' => true, diff --git a/examples/server/eof_server.php b/examples/server/eof_server.php index c78b0278fe0..d901b221d89 100644 --- a/examples/server/eof_server.php +++ b/examples/server/eof_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/exist.php b/examples/server/exist.php deleted file mode 100755 index a9d4b2a2cf6..00000000000 --- a/examples/server/exist.php +++ /dev/null @@ -1,141 +0,0 @@ - 4, - //'open_eof_check' => true, - //'package_eof' => "\r\n", -// 'task_ipc_mode' => 2, - 'task_worker_num' => 2, - 'user' => 'www', - 'group' => 'www', - 'chroot' => '/opt/tmp', - //'task_ipc_mode' => 1, - //'dispatch_mode' => 1, - //'log_file' => '/tmp/swoole.log', - 'heartbeat_check_interval' => 300, - 'heartbeat_idle_time' => 300, - // open_cpu_affinity => 1, - //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 -); - -if (isset($argv[1]) and $argv[1] == 'daemon') { - $config['daemonize'] = true; -} else { - $config['daemonize'] = false; -} - -//$mode = SWOOLE_BASE; -$mode = SWOOLE_PROCESS; - -$serv = new swoole_server("0.0.0.0", 9501, $mode); -$serv->set($config); -/** - * 使用类的静态属性,可以直接访问 - */ -G::$serv = $serv; - -function my_onStart(swoole_server $serv) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: master"); - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; -} - -function my_log($msg) -{ - echo $msg.PHP_EOL; -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onClose($serv, $fd, $reactor_id) -{ - my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: fd=$fd is closed"); - $buffer = G::getBuffer($fd); - if ($buffer) - { - $buffer->clear(); - } - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onConnect(swoole_server $serv, $fd, $reactor_id) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - $serv->task($data . '-' . $fd); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - list($str, $fd) = explode('-', $data); - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL ; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - echo "Task[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; - return $data; -} - -function my_onFinish(swoole_server $serv, $task_id, $data) -{ - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'Send Data To FD[' . $fd . ']'); - echo "Task Finish: result=" . $data . ". PID=" . $serv->worker_pid.PHP_EOL; -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('ManagerStart', function($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: manager"); -}); -$serv->start(); diff --git a/examples/server/getReceivedTime.php b/examples/server/getReceivedTime.php index 9e988d3ebae..e1394d55c2f 100644 --- a/examples/server/getReceivedTime.php +++ b/examples/server/getReceivedTime.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ // echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; //}); @@ -7,7 +7,7 @@ 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { usleep(rand(100000, 2000000)); var_dump(round($serv->getReceivedTime(), 10)); }); diff --git a/examples/server/host_update.php b/examples/server/host_update.php new file mode 100644 index 00000000000..cda874d010d --- /dev/null +++ b/examples/server/host_update.php @@ -0,0 +1,121 @@ +set(array( + 'worker_num' => 2, + //'open_eof_check' => true, + //'package_eof' => "\r\n", + 'task_worker_num' => 2, + //'dispatch_mode' => 2, + //'daemonize' => 1, + //'heartbeat_idle_time' => 5, + //'heartbeat_check_interval' => 5, +)); +function my_onStart($serv) +{ + echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; + echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; +} + +function my_onShutdown($serv) +{ + echo "Server: onShutdown\n"; +} + +function my_onClose($serv, $fd, $reactor_id) +{ + //echo "Client: fd=$fd is closed.\n"; +} + +function my_onConnect($serv, $fd, $reactor_id) +{ + //throw new Exception("hello world"); +// echo "Client:Connect.\n"; +} + + +$class = null; +function my_onWorkerStart($serv, $worker_id) +{ + global $argv; + global $class; + opcache_reset(); + include "hot_update_class.php"; + $class = new HotUpdate(); + if ($worker_id >= $serv->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]} task worker"); + } else { + swoole_set_process_name("php {$argv[0]} event worker"); + } + //echo "WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\n"; +} + +function my_onWorkerStop($serv, $worker_id) +{ + echo "WorkerStop[$worker_id]|pid=" . posix_getpid() . ".\n"; +} + +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) +{ + $cmd = trim($data); + if ($cmd == "reload") { + $serv->reload($serv); + } elseif ($cmd == "task") { + $task_id = $serv->task("hello world", 0); + echo "Dispath AsyncTask: id=$task_id\n"; + } elseif ($cmd == "info") { + $info = $serv->connection_info($fd); + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == "broadcast") { + $start_fd = 0; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if ($conn_list === false) { + break; + } + $start_fd = end($conn_list); + foreach ($conn_list as $conn) { + if ($conn === $fd) { + continue; + } + $serv->send($conn, "hello from $fd\n"); + } + } + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { + hello_no_exists(); + } elseif ($cmd == "shutdown") { + $serv->shutdown(); + } else { + global $class; + $data .= $class->getData(); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + //$serv->close($fd); + } + //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; +} + +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) +{ + echo "AsyncTask[PID=" . posix_getpid() . "]: task_id=$task_id." . PHP_EOL; + $serv->finish("OK"); +} + +function my_onFinish(Swoole\Server $serv, $data) +{ + echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL; +} + +$serv->on('Start', 'my_onStart'); +$serv->on('Connect', 'my_onConnect'); +$serv->on('Receive', 'my_onReceive'); +$serv->on('Close', 'my_onClose'); +$serv->on('Shutdown', 'my_onShutdown'); +$serv->on('WorkerStart', 'my_onWorkerStart'); +$serv->on('WorkerStop', 'my_onWorkerStop'); +$serv->on('Task', 'my_onTask'); +$serv->on('Finish', 'my_onFinish'); +$serv->on('WorkerError', function ($serv, $worker_id, $worker_pid, $exit_code) { + echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; +}); +$serv->start(); diff --git a/examples/hot_update_class.php b/examples/server/hot_update_class.php similarity index 100% rename from examples/hot_update_class.php rename to examples/server/hot_update_class.php diff --git a/examples/server/ip_dispatch.php b/examples/server/ip_dispatch.php index 35cfe7412af..4ca00b743c3 100644 --- a/examples/server/ip_dispatch.php +++ b/examples/server/ip_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->workerid = 0; $serv->set(array( @@ -37,7 +37,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { foreach($serv->fdlist as $_fd=>$val) { $serv->send($_fd, "{$fd} say:".$data.PHP_EOL); diff --git a/examples/server/length_client.php b/examples/server/length_client.php index a0007f6c21c..4fbbb1d7618 100644 --- a/examples/server/length_client.php +++ b/examples/server/length_client.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, 'package_max_length' => 8 * 1024 * 1024, diff --git a/examples/server/length_server.php b/examples/server/length_server.php index 161f58b2e97..c494a66ad1b 100644 --- a/examples/server/length_server.php +++ b/examples/server/length_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/listen_1k_port.php b/examples/server/listen_1k_port.php index e7c6baebfa0..ded093874db 100644 --- a/examples/server/listen_1k_port.php +++ b/examples/server/listen_1k_port.php @@ -1,5 +1,5 @@ on('workerstart', function($server, $id) { global $argv; @@ -7,10 +7,10 @@ $local_listener = stream_socket_server("127.0.0.1", 9999); - swoole_event_add($local_listener, function($server){ + Swoole\Event::add($local_listener, function($server){ $local_client = stream_socket_accept($server); - swoole_event_add($local_client, function($client){ + Swoole\Event::add($local_client, function($client){ echo fread($client, 8192); fwrite($client, "hello"); }); @@ -18,16 +18,16 @@ }); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id) { //echo "connect\n";; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: ".$data); //$serv->close($fd); }); -$serv->on('close', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('close', function (Swoole\Server $serv, $fd, $reactor_id) { //var_dump($serv->connection_info($fd)); //echo "onClose\n"; }); diff --git a/examples/server/manager_timer.php b/examples/server/manager_timer.php index 2d4e697fdb5..25264525e3e 100644 --- a/examples/server/manager_timer.php +++ b/examples/server/manager_timer.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -10,25 +10,25 @@ // sleep(30); - $id = swoole_timer_tick(3000, function () { + $id = Swoole\Timer::tick(3000, function () { echo "timer 1\n"; }); - swoole_timer_after(9000, function () use ($id) { + Swoole\Timer::after(9000, function () use ($id) { echo "timer 2\n"; - swoole_timer_clear($id); + Swoole\Timer::clear($id); - swoole_timer_tick(2000, function () { + Swoole\Timer::tick(2000, function () { echo "timer 3\n"; }); - swoole_timer_tick(300, function () { + Swoole\Timer::tick(300, function () { echo "timer 4\n"; }); }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { diff --git a/examples/server/mixed.php b/examples/server/mixed.php new file mode 100644 index 00000000000..5408899ac09 --- /dev/null +++ b/examples/server/mixed.php @@ -0,0 +1,452 @@ + 16, // 线程数. 一般设置为CPU核数的1-4倍 + 'worker_num' => 2, // 工作进程数量. 设置为CPU的1-4倍最合理 + 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) + 'max_conn' => 10000, // 最大连接数 + 'task_worker_num' => 1, // 任务工作进程数量 +// 'task_ipc_mode' => 2, // 设置 Task 进程与 Worker 进程之间通信的方式。 + 'task_max_request' => 0, // 防止 PHP 内存溢出 + //'task_tmpdir' => '/tmp', + //'message_queue_key' => ftok(SYS_ROOT . 'queue.msg', 1), + 'dispatch_mode' => 2, + //'daemonize' => 1, // 设置守护进程模式 + 'backlog' => 128, + //'log_file' => '/data/logs/swoole.log', + 'heartbeat_check_interval' => 2, // 心跳检测间隔时长(秒) + 'heartbeat_idle_time' => 3, // 连接最大允许空闲的时间 + //'open_eof_check' => 1, + //'open_eof_split' => 1, + //'package_eof' => "\r\r\n", + //'open_cpu_affinity' => 1, + 'socket_buffer_size' => 1024 * 1024 * 128, + 'output_buffer_size' => 1024 * 1024 * 2, + //'enable_delay_receive' => true, + //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 + ); +} + +if (isset($argv[1]) and $argv[1] == 'daemon') { + G::$config['daemonize'] = true; +} else { + G::$config['daemonize'] = false; +} + +//$mode = SWOOLE_BASE; +$mode = SWOOLE_PROCESS; + +$serv = new Swoole\Server("0.0.0.0", '9501', $mode, SWOOLE_SOCK_TCP); +$serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); +$serv->listen('::', 9503, SWOOLE_SOCK_TCP6); +$serv->listen('::', 9504, SWOOLE_SOCK_UDP6); +$process1 = new Swoole\Process(function ($worker) use ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: my_process1"); + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { + echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx + foreach ($serv->connections as $conn) { + $serv->send($conn, "heartbeat\n"); + } + }); + Swoole\Timer::tick(5000, function () use ($serv) { + $serv->sendMessage("hello event worker", 0); + $serv->sendMessage("hello task worker", 4); + }); +}, false); + +//$serv->addprocess($process1); + +$process2 = new Swoole\Process(function ($worker) use ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: my_process2"); + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { + echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx + }); +}, false); + +//$serv->addprocess($process2); + +$serv->set(G::$config); +$serv->set(['reactor_num' => 4]); + +/** + * 使用类的静态属性,可以直接访问 + */ +G::$serv = $serv; + +function my_onStart(Swoole\Server $serv) +{ + global $argv; + swoole_set_process_name("php {$argv[0]}: master"); + my_log("Server: start.Swoole version is [" . SWOOLE_VERSION . "]"); + my_log("MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}"); +} + +function my_log($msg) +{ + global $serv; + if (empty($serv->worker_pid)) { + $serv->worker_pid = posix_getpid(); + } + echo "#" . $serv->worker_pid . "\t[" . date('H:i:s') . "]\t" . $msg . PHP_EOL; +} + +function forkChildInWorker() +{ + global $serv; + echo "on worker start\n"; + $process = new Swoole\Process(function (Swoole\Process $worker) use ($serv) { +// $serv = new Swoole\Server( "0.0.0.0", 9503 ); +// $serv->set(array( +// 'worker_num' => 1 +// )); +// $serv->on ( 'receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { +// $serv->send ( $fd, "Swoole: " . $data ); +// $serv->close ( $fd ); +// }); +// $serv->start (); +// Swoole\Event::add ($worker->pipe, function ($pipe) use ($worker) { +// echo $worker->read()."\n"; +// }); + }); + + $pid = $process->start(); + echo "Fork child process success. pid={$pid}\n"; + //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 + $serv->childprocess = $process; +} + +function processRename(Swoole\Server $serv, $worker_id) +{ + + global $argv; + if ($serv->taskworker) { + swoole_set_process_name("php {$argv[0]}: task"); + } else { + swoole_set_process_name("php {$argv[0]}: worker"); + } +// if ($worker_id == 0) +// { +// var_dump($serv->setting); +// } + my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); +} + +function setTimerInWorker(Swoole\Server $serv, $worker_id) +{ + + if ($worker_id == 0) { + echo "Start: " . microtime(true) . "\n"; + //$serv->addtimer(3000); +// $serv->addtimer(7000); + //var_dump($serv->gettimer()); + } +// $serv->after(2000, function(){ +// echo "Timeout: ".microtime(true)."\n"; +// }); +// $serv->after(5000, function(){ +// echo "Timeout: ".microtime(true)."\n"; +// global $serv; +// $serv->deltimer(3000); +// }); +} + +function my_onShutdown($serv) +{ + echo "Server: onShutdown\n"; +} + +function my_onClose(Swoole\Server $serv, $fd, $reactor_id) +{ + my_log("Client[$fd@$reactor_id]: fd=$fd is closed"); + var_dump($serv->getClientInfo($fd)); +} + +function my_onConnect(Swoole\Server $serv, $fd, $reactor_id) +{ + //throw new Exception("hello world"); +// var_dump($serv->connection_info($fd)); + //var_dump($serv, $fd, $reactor_id); +// echo "Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\n"; + $serv->after(2000, function () use ($serv, $fd) { + $serv->confirm($fd); + }); + my_log("Client: Connect --- {$fd}"); +} + +function timer_show($id) +{ + my_log("Timer#$id"); +} + +function my_onWorkerExit(Swoole\Server $serv, $worker_id) +{ + global $argv; +} + +function my_onWorkerStart(Swoole\Server $serv, $worker_id) +{ + processRename($serv, $worker_id); + + if (!$serv->taskworker) { + Swoole\Process::signal(SIGUSR2, function ($signo) { + echo "SIGNAL: $signo\n"; + }); + Swoole\Event::defer(function () { + echo "defer call\n"; + }); +// $serv->tick(2000, function() use ($serv) { +// echo "Worker-{$serv->worker_id} tick-2000\n"; +// }); + } else { +// Swoole\Timer::after(2000, function() { +// echo "after 2 secends.\n"; +// }); +// $serv->tick(1000, function ($id) use ($serv) { +// if (G::$index > 10) { +// $serv->after(2500, 'timer_show', 2); +// G::$index = 0; +// } else { +// G::$index++; +// } +// timer_show($id); +// }); + } + //forkChildInWorker(); +// setTimerInWorker($serv, $worker_id); +} + +function my_onWorkerStop($serv, $worker_id) +{ + echo "WorkerStop[$worker_id]|pid=" . $serv->worker_pid . ".\n"; +} + +function my_onPacket($serv, $data, $clientInfo) +{ + $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data); + var_dump($clientInfo); +} + +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) +{ + my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); + $cmd = trim($data); + if ($cmd == "reload") { + $serv->reload(); + } elseif ($cmd == "task") { + $task_id = $serv->task("task " . $fd); + echo "Dispath AsyncTask: id=$task_id\n"; + } elseif ($cmd == "taskclose") { + $serv->task("close " . $fd); + echo "close the connection in taskworker\n"; + } elseif ($cmd == "tasksend") { + $serv->task("send " . $fd); + } elseif ($cmd == "bigtask") { + $serv->task(str_repeat('A', 8192 * 5)); + } elseif ($cmd == "taskwait") { + $result = $serv->taskwait("taskwait"); + if ($result) { + $serv->send($fd, "taskwaitok"); + } + echo "SyncTask: result=" . var_export($result, true) . "\n"; + } elseif ($cmd == "taskWaitMulti") { + $result = $serv->taskWaitMulti(array( + str_repeat('A', 8192 * 5), + str_repeat('B', 8192 * 6), + str_repeat('C', 8192 * 8) + )); + if ($result) { + $resp = "taskWaitMulti ok\n"; + foreach ($result as $k => $v) { + $resp .= "result[$k] length=" . strlen($v) . "\n"; + } + $serv->send($fd, $resp); + } else { + $serv->send($fd, "taskWaitMulti error\n"); + } + } elseif ($cmd == "hellotask") { + $serv->task("hellotask"); + } elseif ($cmd == "taskcallback") { + $serv->task("taskcallback", -1, function (Swoole\Server $serv, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + } elseif ($cmd == "sendto") { + $serv->sendto("127.0.0.1", 9999, "hello world"); + } elseif ($cmd == "close") { + $serv->send($fd, "close connection\n"); + $result = $serv->close($fd); + } elseif ($cmd == "info") { + $info = $serv->connection_info(strval($fd), $reactor_id); + var_dump($info["remote_ip"]); + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == 'proxy') { + $serv->send(1, "hello world\n"); + } elseif ($cmd == 'sleep') { + sleep(10); + } elseif ($cmd == 'foreach') { + foreach ($serv->connections as $fd) { + echo "conn : $fd\n"; + } + return; + } elseif ($cmd == 'tick') { + $serv->tick(2000, function ($id) { + echo "tick #$id\n"; + }); + } elseif ($cmd == 'addtimer') { + $serv->addtimer(3000); + } elseif ($cmd == "list") { + $start_fd = 0; + echo "broadcast\n"; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if (empty($conn_list)) { + echo "iterates finished\n"; + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + } + } elseif ($cmd == "list2") { + foreach ($serv->connections as $con) { + var_dump($serv->connection_info($con)); + } + } elseif ($cmd == "stats") { + $serv_stats = $serv->stats(); + $serv->send($fd, 'Stats: ' . var_export($serv_stats, true) . "\ncount=" . count($serv->connections) . PHP_EOL); + } elseif ($cmd == "broadcast") { + broadcast($serv, $fd, "hello from $fd\n"); + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { + hello_no_exists(); + } elseif ($cmd == "exit") { + exit("worker php exit.\n"); + } //关闭fd + elseif (substr($cmd, 0, 5) == "close") { + $close_fd = substr($cmd, 6); + $serv->close($close_fd); + } elseif ($cmd == "shutdown") { + $serv->shutdown(); + } elseif ($cmd == "fatalerror") { + require __DIR__ . '/php/error.php'; + } elseif ($cmd == 'defer') { + $serv->defer(function () use ($fd, $serv) { + $serv->close($fd); + $serv->defer(function () { + echo "deferd\n"; + }); + }); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + } else { + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + //$serv->close($fd); + } + //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; +// $serv->after( +// 800, function () { +// echo "hello"; +// } +// ); + //Swoole\Server_send($serv, $other_fd, "Server: $data", $other_reactor_id); +} + +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) +{ + if ($data == 'taskwait') { + $fd = str_replace('task-', '', $data); + $serv->send($fd, "hello world"); + return array("task" => 'wait'); + } elseif ($data == 'taskcallback') { + return array("task" => 'callback'); + } else { + $cmd = explode(' ', $data); + if ($cmd[0] == 'send') { + $serv->send($cmd[1], str_repeat('A', 10000) . "\n"); + } elseif ($cmd[0] == 'close') { + $serv->close($cmd[1]); + } else { + echo "bigtask: length=" . strlen($data) . "\n"; + return $data; + } +// $serv->sendto('127.0.0.1', 9999, "hello world"); + //Swoole\Timer::after(1000, "test"); +// var_dump($data); +// $serv->send($fd, str_repeat('A', 8192 * 2)); +// $serv->send($fd, str_repeat('B', 8192 * 2)); +// $serv->send($fd, str_repeat('C', 8192 * 2)); +// $serv->send($fd, str_repeat('D', 8192 * 2)); + return; + } + + if ($data == "hellotask") { + broadcast($serv, 0, "hellotask"); + } else { + echo "AsyncTask[PID=" . $serv->worker_pid . "]: task_id=$task_id." . PHP_EOL; + //eg: test-18 + return $data; + } +} + +function my_onFinish(Swoole\Server $serv, $task_id, $data) +{ + list($str, $fd) = explode('-', $data); + $serv->send($fd, 'taskok'); + var_dump($str, $fd); + echo "AsyncTask Finish: result={$data}. PID=" . $serv->worker_pid . PHP_EOL; +} + +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code, $signo) +{ + echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code|Signal=$signo\n"; +} + +function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") +{ + $start_fd = 0; + echo "broadcast\n"; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if ($conn_list === false) { + break; + } + $start_fd = end($conn_list); + foreach ($conn_list as $conn) { + if ($conn === $fd) continue; + $ret1 = $serv->send($conn, $data); + //var_dump($ret1); + //$ret2 = $serv->close($conn); + //var_dump($ret2); + } + } +} + +$serv->on('PipeMessage', function ($serv, $src_worker_id, $msg) { + my_log("PipeMessage: Src={$src_worker_id},Msg=" . trim($msg)); + if ($serv->taskworker) { + $serv->sendMessage("hello user process", + $src_worker_id); + } +}); + +$serv->on('Start', 'my_onStart'); +$serv->on('Connect', 'my_onConnect'); +$serv->on('Receive', 'my_onReceive'); +$serv->on('Packet', 'my_onPacket'); +$serv->on('Close', 'my_onClose'); +$serv->on('Shutdown', 'my_onShutdown'); +$serv->on('WorkerStart', 'my_onWorkerStart'); +$serv->on('WorkerStop', 'my_onWorkerStop'); +$serv->on('Task', 'my_onTask'); +$serv->on('Finish', 'my_onFinish'); +$serv->on('WorkerError', 'my_onWorkerError'); +$serv->on('WorkerExit', 'my_onWorkerExit'); +$serv->on('ManagerStart', function ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: manager"); +}); +$serv->start(); diff --git a/examples/server/multi_instance.php b/examples/server/multi_instance.php index abd632386cc..2a4318075fa 100644 --- a/examples/server/multi_instance.php +++ b/examples/server/multi_instance.php @@ -1,9 +1,9 @@ on("start", function ($server) use ($port) { echo "Swoole http server is started at http://127.0.0.1:{$port}\n"; diff --git a/examples/server/multi_port.php b/examples/server/multi_port.php new file mode 100644 index 00000000000..4c6c462f768 --- /dev/null +++ b/examples/server/multi_port.php @@ -0,0 +1,44 @@ +addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); + +$port->on('packet', function($serv, $data, $client) { + var_dump($data, $client); + $serv->sendto($client['address'], $client['port'], "welcome admin\n"); +}); +$serv->on('connect', function ($serv, $fd) { + echo "Client:Connect.\n"; +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + $info = $serv->connection_info($fd, $reactor_id); + //来自9502的内网管理端口 + if($info['server_port'] == 9502) { + $serv->send($fd, "welcome admin\n"); + $start_fd = 0; + while(true) + { + $conn_list = $serv->connection_list($start_fd, 10); + if($conn_list === false) + { + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + + foreach($conn_list as $conn) + { + if($conn === $fd) continue; + $serv->send($conn, "hello from $fd\n"); + } + } + } + //来自外网 + else { + $serv->send($fd, 'Swoole: '.$data); + } +}); +$serv->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/pipe_message.php b/examples/server/pipe_message.php index 713c8e885a7..827b372f008 100644 --- a/examples/server/pipe_message.php +++ b/examples/server/pipe_message.php @@ -1,6 +1,6 @@ set(array( 'worker_num' => 2, 'task_worker_num' => 2, @@ -10,7 +10,7 @@ echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; }); -$serv->on('task', function (swoole_server $serv, $task_id, $reactor_id, $data){ +$serv->on('task', function (Swoole\Server $serv, $task_id, $reactor_id, $data){ echo "#{$serv->worker_id} NewTask: $data\n"; $serv->sendMessage($data, 0); //$serv->send($fd, str_repeat('B', 1024*rand(40, 60)).rand(10000, 99999)."\n"); @@ -20,7 +20,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); if($cmd == 'totask') { diff --git a/examples/proxy_sync.php b/examples/server/proxy_sync.php similarity index 93% rename from examples/proxy_sync.php rename to examples/server/proxy_sync.php index 9e2cf5ea611..3ec5440bfa1 100644 --- a/examples/proxy_sync.php +++ b/examples/server/proxy_sync.php @@ -7,7 +7,7 @@ class ProxyServer function run() { - $serv = new swoole_server("127.0.0.1", 9509); + $serv = new Swoole\Server("127.0.0.1", 9509); $serv->set(array( 'worker_num' => 32, //reactor thread num 'backlog' => 128, //listen backlog @@ -47,7 +47,7 @@ function onConnect($serv, $fd, $reactor_id) function onReceive($serv, $fd, $reactor_id, $data) { - $socket = new swoole_client(SWOOLE_SOCK_TCP); + $socket = new Swoole\Client(SWOOLE_SOCK_TCP); if($socket->connect('127.0.0.1', 80, 0.5)) { $socket->send($data); diff --git a/examples/redis_pool.php b/examples/server/redis_pool.php similarity index 98% rename from examples/redis_pool.php rename to examples/server/redis_pool.php index be6b0d98a8f..12e79d6976b 100644 --- a/examples/redis_pool.php +++ b/examples/server/redis_pool.php @@ -18,7 +18,7 @@ throw new Exception("install redis extension, pecl install redis"); } -$serv = new swoole_server("0.0.0.0", 9508); +$serv = new Swoole\Server("0.0.0.0", 9508); $serv->set(array( 'worker_num' => 4,//base on you cpu nums diff --git a/examples/server/reload_aysnc.php b/examples/server/reload_aysnc.php index a66b194c37c..a5ecbd18d5b 100644 --- a/examples/server/reload_aysnc.php +++ b/examples/server/reload_aysnc.php @@ -1,5 +1,5 @@ set([ 'worker_num' => 4, @@ -13,7 +13,7 @@ if ($serv->taskworker) { return; } - swoole_event::add(STDIN, function () use ($wid) { + Swoole\Event::add(STDIN, function () use ($wid) { $data = fread(STDIN, 8192); if ($data) { echo "#{$wid}: $data"; @@ -21,17 +21,17 @@ }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ $serv->on('WorkerExit', function ($serv, $wid) { echo "WorkerExit, PID=".posix_getpid()."\t$wid\n"; - swoole_event::del(STDIN); + Swoole\Event::del(STDIN); }); $serv->start(); diff --git a/examples/server/reload_force.php b/examples/server/reload_force.php index 03f8536d628..9bd5b3b70e6 100644 --- a/examples/server/reload_force.php +++ b/examples/server/reload_force.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/reload_force2.php b/examples/server/reload_force2.php index 50873d568eb..785b82f5299 100644 --- a/examples/server/reload_force2.php +++ b/examples/server/reload_force2.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/send_1m.php b/examples/server/send_1m.php new file mode 100644 index 00000000000..4a416f9e3f0 --- /dev/null +++ b/examples/server/send_1m.php @@ -0,0 +1,30 @@ +set(array('worker_num' => 1)); +$serv->on('workerStart', function($serv, $worker_id) { + //if($worker_id == 0) $serv->addtimer(500); +}); +$serv->on('connect', function ($serv, $fd, $reactor_id){ + $serv->array['fd'] = &strval($fd); + echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; +}); +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + //echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; + $array = array('A', 'B', 'C', 'D', 'E', 'F', 'G'); + $data = ''; + $n_bytes = 0; + for ($i = 0; $i < 10; $i++) + { + $_str = str_repeat($array[$i % 7], 4030) . "\n"; + //$serv->send($fd, $_str); + $n_bytes += strlen($_str); + $data .= $_str; + } + echo "send " . $n_bytes . " bytes\n"; + $serv->send( $serv->array['fd'], $data); + $serv->close($fd); +}); +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/sendfile.php b/examples/server/sendfile.php new file mode 100644 index 00000000000..1b87c62155f --- /dev/null +++ b/examples/server/sendfile.php @@ -0,0 +1,24 @@ +set(array( + 'worker_num' => 1, +)); +$serv->on('timer', function($serv, $interval) { + echo "onTimer: $interval\n"; +}); +$serv->on('workerStart', function($serv, $worker_id) { + //if($worker_id == 0) $serv->addtimer(300); +}); +$serv->on('connect', function (Swoole\Server $serv, $fd){ + $serv->send($fd, filesize(__DIR__.'/test.jpg')); + //echo "Client:Connect.\n"; +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + echo "Client[$fd]: $data\n"; + $serv->sendfile($fd, __DIR__.'/test.jpg'); + //$serv->close($fd); +}); +$serv->on('close', function ($serv, $fd) { + //echo "Client: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/single.php b/examples/server/single.php index 83f9b8e32e1..b1fea489695 100644 --- a/examples/server/single.php +++ b/examples/server/single.php @@ -1,6 +1,6 @@ on('Receive', function($serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: $data"); diff --git a/examples/server/tcp_client.php b/examples/server/tcp_client.php index 0ed5215a01a..9b0ed877ef4 100644 --- a/examples/server/tcp_client.php +++ b/examples/server/tcp_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9504)) { exit("connect failed\n"); diff --git a/examples/server/tcp_server.php b/examples/server/tcp_server.php index 9ebb17e8513..f3e8dc687b6 100644 --- a/examples/server/tcp_server.php +++ b/examples/server/tcp_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/trace.php b/examples/server/trace.php index c35ab10b03c..a2c923dacdf 100644 --- a/examples/server/trace.php +++ b/examples/server/trace.php @@ -10,7 +10,7 @@ function test_sleep() sleep(5); } -$server = new swoole_server('127.0.0.1', 9501); +$server = new Swoole\Server('127.0.0.1', 9501); $server->set([ 'worker_num' => 1, @@ -29,13 +29,13 @@ function test_sleep() $serv->send($fd, "Swoole: $data"); }); -$server->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; test(); $serv->send($data, "Swoole: task\n"); }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/server/uid_dispatch.php b/examples/server/uid_dispatch.php index cdd82661d2d..c2f5ad94d3d 100644 --- a/examples/server/uid_dispatch.php +++ b/examples/server/uid_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->set(array( //'tcp_defer_accept' => 5, @@ -36,7 +36,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $conn = $serv->connection_info($fd); print_r($conn); echo "worker_id: " . $serv->worker_id . PHP_EOL; diff --git a/examples/server/unix_stream.php b/examples/server/unix_stream.php index 665259c3e2f..38e532d829e 100644 --- a/examples/server/unix_stream.php +++ b/examples/server/unix_stream.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); //$serv->close($fd); diff --git a/examples/server/zmq.php b/examples/server/zmq.php index 3c00e72861a..5455abd8a87 100644 --- a/examples/server/zmq.php +++ b/examples/server/zmq.php @@ -1,5 +1,5 @@ getsockopt(ZMQ::SOCKOPT_FD); - swoole_event_add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); + Swoole\Event::add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); echo "worker start\n"; }); @@ -37,7 +37,7 @@ function onZMQR() echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; diff --git a/examples/server_hot_update_opcache.php b/examples/server_hot_update_opcache.php deleted file mode 100644 index c5d1e196579..00000000000 --- a/examples/server_hot_update_opcache.php +++ /dev/null @@ -1,144 +0,0 @@ -set(array( - 'worker_num' => 2, - //'open_eof_check' => true, - //'package_eof' => "\r\n", - 'task_worker_num' => 2, - //'dispatch_mode' => 2, - //'daemonize' => 1, - //'heartbeat_idle_time' => 5, - //'heartbeat_check_interval' => 5, -)); -function my_onStart($serv) -{ - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; - //$serv->addtimer(1000); -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onTimer($serv, $interval) -{ - echo "Server:Timer Call.Interval=$interval\n"; -} - -function my_onClose($serv, $fd, $reactor_id) -{ - //echo "Client: fd=$fd is closed.\n"; -} - -function my_onConnect($serv, $fd, $reactor_id) -{ - //throw new Exception("hello world"); -// echo "Client:Connect.\n"; -} - - -$class = null; -function my_onWorkerStart($serv, $worker_id) -{ - global $argv; - global $class; - opcache_reset(); - include "hot_update_class.php"; - $class = new HotUpdate(); - if($worker_id >= $serv->setting['worker_num']) { - swoole_set_process_name("php {$argv[0]} task worker"); - } else { - swoole_set_process_name("php {$argv[0]} event worker"); - } - //echo "WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\n"; - //$serv->addtimer(500); //500ms -} - -function my_onWorkerStop($serv, $worker_id) -{ - echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - $cmd = trim($data); - if($cmd == "reload") - { - $serv->reload($serv); - } - elseif($cmd == "task") - { - $task_id = $serv->task("hello world", 0); - echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif($cmd == "info") - { - $info = $serv->connection_info($fd); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif($cmd == "broadcast") - { - $start_fd = 0; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $serv->send($conn, "hello from $fd\n"); - } - } - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { - hello_no_exists(); - } - elseif($cmd == "shutdown") - { - $serv->shutdown(); - } - else - { - global $class; - $data .= $class->getData(); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - //$serv->close($fd); - } - //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; - //$serv->deltimer(800); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - echo "AsyncTask[PID=".posix_getpid()."]: task_id=$task_id.".PHP_EOL; - $serv->finish("OK"); -} - -function my_onFinish(swoole_server $serv, $data) -{ - echo "AsyncTask Finish:Connect.PID=".posix_getpid().PHP_EOL; -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Timer', 'my_onTimer'); -$serv->on('WorkerStart', 'my_onWorkerStart'); -$serv->on('WorkerStop', 'my_onWorkerStop'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('WorkerError', function($serv, $worker_id, $worker_pid, $exit_code) { - echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; -}); -$serv->start(); diff --git a/examples/set_cpu_affinity.php b/examples/set_cpu_affinity.php deleted file mode 100644 index c44f3adf6bd..00000000000 --- a/examples/set_cpu_affinity.php +++ /dev/null @@ -1,3 +0,0 @@ -on("connect", function (swoole_client $cli) -{ - $cli->send("Hello World\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->close(); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/ssl/client.php b/examples/ssl/client.php index d4bbf0f9cd3..3fee823cc50 100644 --- a/examples/ssl/client.php +++ b/examples/ssl/client.php @@ -1,5 +1,5 @@ set(array( 'ssl_cert_file' => __DIR__.'/ca/client-cert.pem', diff --git a/examples/ssl/passphrase.php b/examples/ssl/passphrase.php index bf62abe0206..ac7b9ab979f 100644 --- a/examples/ssl/passphrase.php +++ b/examples/ssl/passphrase.php @@ -1,5 +1,5 @@ set(array( "ssl_key_file" => __DIR__ . '/ssl.key', "ssl_cert_file" => __DIR__ . '/ssl.crt', diff --git a/examples/ssl/server.php b/examples/ssl/server.php index fdd3cf50a44..d82fc116a71 100644 --- a/examples/ssl/server.php +++ b/examples/ssl/server.php @@ -1,6 +1,6 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_TCP); @@ -18,13 +18,13 @@ 'ssl_verify_depth' => 10, )); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id){ +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; $info = $serv->getClientInfo($fd); var_dump($info); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, "Swoole: $data\n"); }); diff --git a/examples/ssl/swoole.log b/examples/ssl/swoole.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/ssl/webserver.php b/examples/ssl/webserver.php index a3f794ad86e..9ec72146c31 100644 --- a/examples/ssl/webserver.php +++ b/examples/ssl/webserver.php @@ -1,6 +1,6 @@ set([ 'ssl_cert_file' => __DIR__ . '/ssl.crt', 'ssl_key_file' => __DIR__ . '/ssl.key', @@ -12,7 +12,7 @@ ]); //c158354564362fcc -$serv->on('Request', function(swoole_http_request $request, swoole_http_response $response) { +$serv->on('Request', function(Swoole\Http\Request $request, Swoole\Http\Response $response) { //var_dump($request->get); //var_dump($request->post); //var_dump($request->cookie); diff --git a/examples/ssl/websocket_server.php b/examples/ssl/websocket_server.php index ec191a2eed7..065842d24f9 100644 --- a/examples/ssl/websocket_server.php +++ b/examples/ssl/websocket_server.php @@ -1,7 +1,7 @@ set([ 'ssl_cert_file' => $ssl_dir . '/ssl.crt', 'ssl_key_file' => $ssl_dir . '/ssl.key', @@ -18,7 +18,7 @@ echo "client {$fd} connect\n"; }); -$serv->on('open', function (swoole_websocket_server $_server, swoole_http_request $request) { +$serv->on('open', function (Swoole\WebSocket\Server $_server, Swoole\Http\Request $request) { echo "server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\n"; // var_dump($request); }); @@ -27,7 +27,7 @@ $resp->end(file_get_contents(__DIR__.'/websocket_client.html')); }); -$serv->on('message', function (swoole_websocket_server $_server, $frame) { +$serv->on('message', function (Swoole\WebSocket\Server $_server, $frame) { var_dump($frame->data); echo "received ".strlen($frame->data)." bytes\n"; $_send = str_repeat('B', rand(100, 800)); diff --git a/examples/swoole_http_client.php b/examples/swoole_http_client.php deleted file mode 100644 index 3b21681f990..00000000000 --- a/examples/swoole_http_client.php +++ /dev/null @@ -1,51 +0,0 @@ -set([ - //'worker_num' => 2, -]); -$i = 0; -$http->on('request', function ($request, swoole_http_response $response)use(&$i) { - - $route = $request->server['request_uri']; - if($route == '/info'){ - $response->end(json_encode($request)); - return; - } - - $cli = new swoole_http_client('127.0.0.1', 9501); - $cli->set([ - 'timeout' => 0.3, - 'keep_alive' => 1, - ]); - //post request - $cli->setData(http_build_query(['a'=>123,'b'=>"哈哈"])); - $cli->setHeaders(['User-Agent' => "swoole"]); - $cli->on('close', function($cli)use($response){ - // echo "close\n"; - }); - $cli->on('error', function($cli) use ($response){ - $response->end("error"); - }); - $cli->execute('/info', function($cli)use( $response, &$i){ - $cli->setHeaders(['User-Agent' => "swoole"]); - //get request - $cli->execute('/info', function($cli)use($response, &$i){ - $ret = json_encode($cli->headers) . "\nSERVER RESPONSE: ". $cli->body; - $response->end($ret); - $cli->close(); - }); - }); - - - if($i++ == 1000){ - echo "----->Mem: ", memory_get_usage(), "b\n"; - $i = 0; - } - -}); - -$http->start(); diff --git a/examples/table/deadlock.php b/examples/table/deadlock.php index fb17279d075..89517873811 100644 --- a/examples/table/deadlock.php +++ b/examples/table/deadlock.php @@ -2,7 +2,7 @@ ini_set('memory_limit', '8M'); $table = new Swoole\Table(1024); -$table->column('name', swoole_table::TYPE_STRING, 1024 * 64); +$table->column('name', Swoole\Table::TYPE_STRING, 1024 * 64); $table->create(); $table->set('key1', ['name' => str_repeat('A', 1024 * 64 - 1) . "\n"]); diff --git a/examples/table/iterator.php b/examples/table/iterator.php index 2f77712879a..475fd19d1e6 100644 --- a/examples/table/iterator.php +++ b/examples/table/iterator.php @@ -1,8 +1,8 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); $table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango1', 'num' => 3.1415)); diff --git a/examples/table/server.php b/examples/table/server.php index b93ec616f20..4f56336441a 100644 --- a/examples/table/server.php +++ b/examples/table/server.php @@ -1,11 +1,11 @@ column('fd', swoole_table::TYPE_INT); -$table->column('reactor_id', swoole_table::TYPE_INT); -$table->column('data', swoole_table::TYPE_STRING, 64); +$table = new Swoole\Table(1024); +$table->column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); $table->create(); -$serv = new swoole_server('127.0.0.1', 9501); +$serv = new Swoole\Server('127.0.0.1', 9501); $serv->set(['dispatch_mode' => 2]); $serv->table = $table; diff --git a/examples/table/set.php b/examples/table/set.php index bea34127aa2..b4ab0b5a252 100644 --- a/examples/table/set.php +++ b/examples/table/set.php @@ -1,11 +1,11 @@ column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); -//$worker = new swoole_process('child1', false, false); +//$worker = new Swoole\Process('child1', false, false); //$worker->start(); // //child diff --git a/examples/table/simulation.php b/examples/table/simulation.php index 367acd528a2..770ba67ccb1 100644 --- a/examples/table/simulation.php +++ b/examples/table/simulation.php @@ -1,11 +1,11 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); while (true) { diff --git a/examples/table/usage.php b/examples/table/usage.php index 67cf582ade7..9ca36c7a859 100644 --- a/examples/table/usage.php +++ b/examples/table/usage.php @@ -1,8 +1,8 @@ column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); $table->set('a', array('id' => 1, 'name' => 'swoole-co-uk', 'num' => 3.1415)); diff --git a/examples/task/http.php b/examples/task/http.php index affbcf9d71a..83402fd8f6c 100644 --- a/examples/task/http.php +++ b/examples/task/http.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 1, @@ -19,13 +19,13 @@ }); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ } }); -$serv->on('workerStop', function (swoole_server $serv, $id) { +$serv->on('workerStop', function (Swoole\Server $serv, $id) { echo "stop\n"; var_dump($id); }); diff --git a/examples/task/shared_client.php b/examples/task/shared_client.php index b174fb1a283..171b37aaa1c 100644 --- a/examples/task/shared_client.php +++ b/examples/task/shared_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/task/shared_server.php b/examples/task/shared_server.php index 3b276a61eec..9cfb72177bb 100644 --- a/examples/task/shared_server.php +++ b/examples/task/shared_server.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -51,7 +51,7 @@ function my_onWorkerStop($serv, $worker_id) echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; } -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $rdata) { $data = unserialize($rdata); if (isset($data['cmd'])) @@ -79,7 +79,7 @@ function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) } } -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { static $datas = array(); if (isset($data['cmd'])) @@ -112,12 +112,12 @@ function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) // $serv->finish("OK"); } -function my_onFinish(swoole_server $serv, $task_id, $from_worker_id, $data) +function my_onFinish(Swoole\Server $serv, $task_id, $from_worker_id, $data) { echo "AsyncTask Finish: Connect.PID=" . posix_getpid() . PHP_EOL; } -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code) +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code) { echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; } diff --git a/examples/task/task.php b/examples/task/task.php index 8d2d3867134..9435fc693c6 100644 --- a/examples/task/task.php +++ b/examples/task/task.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,7 +9,7 @@ //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -28,13 +28,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); // return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_coro.php b/examples/task/task_coro.php index 3224b8dcb46..79cd927663d 100644 --- a/examples/task/task_coro.php +++ b/examples/task/task_coro.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 4, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); @@ -17,7 +17,7 @@ var_dump($results); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [ID={$serv->worker_id}]: task_id=$task_id, data=$data, data_len=".strlen($data).".".PHP_EOL; //测试超时 if ($serv->worker_id % 4 == 3) @@ -35,7 +35,7 @@ return "hello world.[{$data}]"; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_num.php b/examples/task/task_num.php index 599c4a63a0b..7a673c158cc 100644 --- a/examples/task/task_num.php +++ b/examples/task/task_num.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 2, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = intval($data); for($i=0;$i<$data;$i++) { @@ -16,7 +16,7 @@ } }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [PID=".posix_getpid()."]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; sleep(10); //$serv->finish($data); @@ -24,11 +24,11 @@ return; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); -$serv->on('Timer', function(swoole_server $serv, $time) { +$serv->on('Timer', function(Swoole\Server $serv, $time) { echo "{$time} call".PHP_EOL; print_r($serv->stats()); }); diff --git a/examples/task/task_queue.php b/examples/task/task_queue.php index 0e872ec8c6f..1da9257d16e 100644 --- a/examples/task/task_queue.php +++ b/examples/task/task_queue.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,15 +9,15 @@ 'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: TASK_ID=$task_id]\n"; var_dump($data); }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_stream.php b/examples/task/task_stream.php index 90108ea0dcf..10b5d6ff8fe 100644 --- a/examples/task/task_stream.php +++ b/examples/task/task_stream.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -8,7 +8,7 @@ // 'message_queue_key' => 0x70001001, )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -27,13 +27,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/test_buffer.php b/examples/test_buffer.php deleted file mode 100644 index 02f766c6e66..00000000000 --- a/examples/test_buffer.php +++ /dev/null @@ -1,39 +0,0 @@ -connect('127.0.0.1', 9501)) -{ - exit("connect fail\n"); -} - -for($i=0; $i<$loop; $i++) -{ - $client->send(str_repeat("A", 8000).$i."[0]"); - //$client->send(str_repeat("A", 20).$i."[1]"); - //$client->send(str_repeat("A", 30).$i."[2]"); - //$ret = $client->send("GET / HTTP/1.1\r\n"); - //$client->send("Host: localhost\r\n"); - //$client->send("Connection: keep-alive\r\n"); - $client->send("\r\n\r\n"); - - //$data = $client->recv(1024, 0); - //if($data === false) - //{ - // echo "#{$i} recv fail.break\n"; -// break; -// } - //echo "recv[$i]",$data,"\n"; -} - -sleep(1000); -echo "$i: ",$data,"\n"; -echo "test ok. use".((microtime(true) - $_s)*1000)."ms\n"; diff --git a/examples/thread/aio.php b/examples/thread/aio.php new file mode 100644 index 00000000000..e55972f01d0 --- /dev/null +++ b/examples/thread/aio.php @@ -0,0 +1,42 @@ +join(); + } + var_dump($atomic->get()); + sleep(2); + + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); + var_dump($atomic->get()); +} else { + $atomic = $args[1]; + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); +} diff --git a/examples/thread/argv.php b/examples/thread/argv.php new file mode 100644 index 00000000000..a224b95da7f --- /dev/null +++ b/examples/thread/argv.php @@ -0,0 +1,17 @@ +join(); + } +} else { + var_dump($args[0], $args[1], $args[2]); + sleep(1); +} diff --git a/examples/thread/array.php b/examples/thread/array.php new file mode 100644 index 00000000000..1df20fb66c7 --- /dev/null +++ b/examples/thread/array.php @@ -0,0 +1,20 @@ +join(); + } + var_dump($a1->get(), $a2->get()); +} else { + $a1 = $args[1]; + $a2 = $args[2]; + + $a1->add(3); + $a2->add(7); +} diff --git a/examples/thread/benchmark.php b/examples/thread/benchmark.php new file mode 100644 index 00000000000..5a52e6df5a5 --- /dev/null +++ b/examples/thread/benchmark.php @@ -0,0 +1,21 @@ +id); +//var_dump($t2->id); +echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; + +try { + var_dump($list[999]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'out of range')); +} + +try { + unset($list[0]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'unsupported')); +} + +$t1->join(); +$t2->join(); + + diff --git a/examples/thread/exit.php b/examples/thread/exit.php new file mode 100644 index 00000000000..5d1c071f6d8 --- /dev/null +++ b/examples/thread/exit.php @@ -0,0 +1,21 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + sleep(1); + exit(234); +} diff --git a/examples/thread/hook.php b/examples/thread/hook.php new file mode 100644 index 00000000000..de55b63bb2c --- /dev/null +++ b/examples/thread/hook.php @@ -0,0 +1,24 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + sleep(1); + Swoole\Runtime::enableCoroutine(0); + exit(234); +} diff --git a/examples/thread/lock.php b/examples/thread/lock.php new file mode 100644 index 00000000000..e18674d6314 --- /dev/null +++ b/examples/thread/lock.php @@ -0,0 +1,19 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + $lock = $args[0]; + sleep(1); + $lock->unlock(); +} diff --git a/examples/thread/map.php b/examples/thread/map.php new file mode 100644 index 00000000000..fc38de69ffd --- /dev/null +++ b/examples/thread/map.php @@ -0,0 +1,18 @@ + random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => uniqid(), + 'd' => time(), + ]; + + $map = new Thread\Map($array); + $thread = new Thread(__FILE__, $map); +} else { + $map = $args[0]; + var_dump($map->toArray()); +} diff --git a/examples/thread/mt.php b/examples/thread/mt.php new file mode 100644 index 00000000000..ca14d152f26 --- /dev/null +++ b/examples/thread/mt.php @@ -0,0 +1,27 @@ +keys()); + +$list[] = uniqid('swoole'); +$list[count($list)] = uniqid('php'); + +var_dump($args); + +echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $list[0] . "\n"; +var_dump(count($list)); + +//if ($args[0] == 'thread-2') { +// $t3 = new Swoole\Thread('mt.php', 'thread-3', PHP_OS); +// $t3->join(); +//} + +//sleep(5); +//echo "end\n"; diff --git a/examples/thread/nested_map.php b/examples/thread/nested_map.php new file mode 100644 index 00000000000..daa0af5f9c2 --- /dev/null +++ b/examples/thread/nested_map.php @@ -0,0 +1,21 @@ + uniqid(), + 'b' => random_int(1000, 9999), +]; + +var_dump($map['map1']['key1']); +var_dump($map['list1'][0]); + +var_dump($map['list1']->toArray()); + +var_dump($map['map2']); diff --git a/examples/thread/pipe.php b/examples/thread/pipe.php new file mode 100644 index 00000000000..2e806cf17a1 --- /dev/null +++ b/examples/thread/pipe.php @@ -0,0 +1,20 @@ +recv(8192), PHP_EOL; + $thread->join(); + }); +} else { + $sockets = $args[0]; + Co\run(function () use ($sockets) { + sleep(1); + $sockets[1]->send(uniqid()); + }); +} diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php new file mode 100644 index 00000000000..85a736214ca --- /dev/null +++ b/examples/thread/run_test.php @@ -0,0 +1,16 @@ +join(); +} + diff --git a/examples/thread/server.php b/examples/thread/server.php new file mode 100644 index 00000000000..79435f73da7 --- /dev/null +++ b/examples/thread/server.php @@ -0,0 +1,25 @@ +join(); + } +} else { + $http = new Swoole\Http\Server("0.0.0.0", 9503); + $http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->end('hello world'); + }); + $http->start(); +} diff --git a/examples/thread/signal.php b/examples/thread/signal.php new file mode 100644 index 00000000000..b41930b693a --- /dev/null +++ b/examples/thread/signal.php @@ -0,0 +1,36 @@ +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + echo "child thread\n"; + $sockets = $args[0]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); +} diff --git a/examples/thread/socket.php b/examples/thread/socket.php new file mode 100644 index 00000000000..05f57cff1d2 --- /dev/null +++ b/examples/thread/socket.php @@ -0,0 +1,21 @@ +join(); +} else { + $map = $args[0]; + $sock = $map['socket']; + $retval = socket_connect($sock, '127.0.0.1', 80); +} diff --git a/examples/thread/test.php b/examples/thread/test.php new file mode 100644 index 00000000000..3c4b8bc1a20 --- /dev/null +++ b/examples/thread/test.php @@ -0,0 +1,17 @@ +uuid = uniqid(); +$map['obj'] = $o; + +var_dump($map['obj']); + +$s = serialize($map); +var_dump(unserialize($s)); + diff --git a/examples/thread/thread_pool.php b/examples/thread/thread_pool.php new file mode 100644 index 00000000000..226f14a5a9e --- /dev/null +++ b/examples/thread/thread_pool.php @@ -0,0 +1,37 @@ +push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php new file mode 100644 index 00000000000..ddb19ec32af --- /dev/null +++ b/examples/thread/thread_server.php @@ -0,0 +1,67 @@ +set([ + 'worker_num' => 2, + 'task_worker_num' => 3, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL, +// 'trace_flags' => SWOOLE_TRACE_SERVER, +// 'log_level' => SWOOLE_LOG_TRACE, + 'init_arguments' => function () use ($http) { + $map = new Swoole\Thread\Map; + return [$map]; + } +]); + +$http->on('Request', function ($req, $resp) use ($http) { +// $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); + if ($req->server['request_uri'] == '/task') { + $http->task(['code' => uniqid()]); + } elseif ($req->server['request_uri'] == '/stop') { + var_dump($http->getWorkerId()); + var_dump($req->get['worker_id']); + $http->stop($req->get['worker_id'] ?? 0); + } elseif ($req->server['request_uri'] == '/msg') { + $dstWorkerId = random_int(0, 4); + if ($dstWorkerId != $http->getWorkerId()) { + $http->sendMessage('hello ' . base64_encode(random_bytes(16)), $dstWorkerId); + echo "[worker#" . $http->getWorkerId() . "]\tsend pipe message to " . $dstWorkerId . "\n"; + } + } + $resp->end('hello world'); +}); + +$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) { + echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; +}); + +$http->addProcess(new \Swoole\Process(function () { + echo "user process, id=" . \Swoole\Thread::getId() . "\n"; + sleep(2); +})); + +$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { + var_dump($taskId, $srcWorkerId, $data); + return ['result' => uniqid()]; +}); + +$http->on('Finish', function ($server, $taskId, $data) { + var_dump($taskId, $data); +}); + +$http->on('workerStart', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$http->on('workerStop', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is stopped.\n"; +}); + +$http->on('workerExit', function (Server $serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is exited, event_num=" . Swoole\Coroutine::stats()['event_num'] . ".\n"; +}); + +$http->start(); diff --git a/examples/timer/after.php b/examples/timer/after.php index eca3d5c3a74..039864df96a 100644 --- a/examples/timer/after.php +++ b/examples/timer/after.php @@ -4,24 +4,24 @@ function timeout($tm) echo time() . ": Timeout #$tm\n"; if ($tm == 5) { - swoole_timer_after(3000, 'timeout', 7); + Swoole\Timer::after(3000, 'timeout', 7); } } -$timer1 = swoole_timer_after(1000, function () { +$timer1 = Swoole\Timer::after(1000, function () { timeout(1); global $timer1, $timer3; - swoole_timer_clear($timer1); - swoole_timer_clear($timer3); + Swoole\Timer::clear($timer1); + Swoole\Timer::clear($timer3); }); -$timer2 = swoole_timer_after(2000, 'timeout', 2); -$timer3 = swoole_timer_after(4000, 'timeout', 3); -$timer4 = swoole_timer_after(8000, 'timeout', 4); -$timer5 = swoole_timer_after(10000, 'timeout', 5); -$timer6 = swoole_timer_after(5000, 'timeout', 6); +$timer2 = Swoole\Timer::after(2000, 'timeout', 2); +$timer3 = Swoole\Timer::after(4000, 'timeout', 3); +$timer4 = Swoole\Timer::after(8000, 'timeout', 4); +$timer5 = Swoole\Timer::after(10000, 'timeout', 5); +$timer6 = Swoole\Timer::after(5000, 'timeout', 6); var_dump($timer1, $timer2, $timer3, $timer4, $timer5, $timer6); -swoole_process::signal(SIGTERM, function() { - swoole_event_exit(); +Swoole\Process::signal(SIGTERM, function() { + Swoole\Event::exit(); }); diff --git a/examples/timer/clear.php b/examples/timer/clear.php index aac2163abea..1a4a51bf8bf 100644 --- a/examples/timer/clear.php +++ b/examples/timer/clear.php @@ -1,10 +1,10 @@ false ]); -swoole_timer_tick(1000, function () { +Swoole\Timer::tick(1000, function () { $uid = Co::getuid(); assert($uid === -1); echo "#{$uid}\n"; diff --git a/examples/timer/tick.php b/examples/timer/tick.php index 430a34b61c3..029dcd77aa1 100644 --- a/examples/timer/tick.php +++ b/examples/timer/tick.php @@ -3,21 +3,21 @@ function timeout($tm) { echo time() . ": Timeout #$tm\n"; } -$timer1 = swoole_timer_tick(1000, 'timeout', 1); -$timer2 = swoole_timer_tick(2000, 'timeout', 2); +$timer1 = Swoole\Timer::tick(1000, 'timeout', 1); +$timer2 = Swoole\Timer::tick(2000, 'timeout', 2); -swoole_timer_tick(3000, function($id) { +Swoole\Timer::tick(3000, function($id) { timeout($id); - //swoole_timer_clear($id); + //Swoole\Timer::clear($id); static $remove = true; if ($remove) { global $timer1; - swoole_timer_clear($timer1); - swoole_timer_tick(7000, 'timeout', 7); + Swoole\Timer::clear($timer1); + Swoole\Timer::tick(7000, 'timeout', 7); $remove = false; } }); -$timer4 = swoole_timer_tick(4000, 'timeout', 4); -$timer5 = swoole_timer_tick(5000, 'timeout', 5); -$timer6 = swoole_timer_tick(6000, 'timeout', 6); +$timer4 = Swoole\Timer::tick(4000, 'timeout', 4); +$timer5 = Swoole\Timer::tick(5000, 'timeout', 5); +$timer6 = Swoole\Timer::tick(6000, 'timeout', 6); diff --git a/examples/udp/async_client.php b/examples/udp/async_client.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/udp/async_client.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/udp/client.php b/examples/udp/client.php index 7f04980c572..5336a9593ef 100644 --- a/examples/udp/client.php +++ b/examples/udp/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9905); $client->send(serialize(['hello' => str_repeat('A', 600), 'rand' => rand(1, 100)])); echo $client->recv() . "\n"; diff --git a/examples/udp/server.php b/examples/udp/server.php index eb5ca83ec7b..dd2edcb608e 100644 --- a/examples/udp/server.php +++ b/examples/udp/server.php @@ -1,12 +1,12 @@ listen('0.0.0.0', 9906 + $i, SWOOLE_SOCK_UDP); } $server->set(['worker_num' => 4]); -$server->on('Packet', function (swoole_server $serv, $data, $addr) +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data", $addr['server_socket']); }); diff --git a/examples/unixsock/async_client.php b/examples/unixsock/async_client.php deleted file mode 100644 index c1f39559908..00000000000 --- a/examples/unixsock/async_client.php +++ /dev/null @@ -1,28 +0,0 @@ -on("connect", function (swoole_client $cli) -{ - $cli->send("GET / HTTP/1.1\r\n\r\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->send(str_repeat('A', 100) . "\n"); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error: [" . $cli->errCode . "] " . socket_strerror($cli->errCode) . "\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect(__DIR__ . '/svr.sock', 0, -1); - -swoole_event_wait(); -echo "exit\n"; diff --git a/examples/unixsock/dgram_client.php b/examples/unixsock/dgram_client.php index 1e53d33620b..7cf5d535083 100644 --- a/examples/unixsock/dgram_client.php +++ b/examples/unixsock/dgram_client.php @@ -1,5 +1,5 @@ connect(__DIR__ . '/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/dgram_server.php b/examples/unixsock/dgram_server.php index 140e5a04cb7..c3efa5ff682 100644 --- a/examples/unixsock/dgram_server.php +++ b/examples/unixsock/dgram_server.php @@ -1,17 +1,17 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, //'daemonize' => true, //'log_file' => '/tmp/swoole.log' )); -//$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +//$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { // echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; // $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)).PHP_EOL); //}); -$serv->on('Packet', function (swoole_server $serv, $data, $addr) { +$serv->on('Packet', function (Swoole\Server $serv, $data, $addr) { //echo "[#".posix_getpid()."]\tClient[{$addr['address']}]: $data\n"; var_dump($addr); $serv->send($addr['address'], json_encode(array("hello" => $data, "addr" => $addr)).PHP_EOL); diff --git a/examples/unixsock/stream_client.php b/examples/unixsock/stream_client.php index a16fcf58d01..bb2b621c2db 100644 --- a/examples/unixsock/stream_client.php +++ b/examples/unixsock/stream_client.php @@ -1,5 +1,5 @@ connect(__DIR__.'/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/stream_server.php b/examples/unixsock/stream_server.php index 7068c487b7a..36a46019859 100644 --- a/examples/unixsock/stream_server.php +++ b/examples/unixsock/stream_server.php @@ -1,5 +1,5 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, @@ -19,7 +19,7 @@ echo "Close, client={$fd}\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#" . posix_getpid() . "]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)) . PHP_EOL); diff --git a/examples/weather_server.php b/examples/weather_server.php deleted file mode 100644 index c7ce1a31060..00000000000 --- a/examples/weather_server.php +++ /dev/null @@ -1,201 +0,0 @@ -setting = $setting; - } - - public function init() - { - - $this->http = new swoole_http_server($this->setting['host'], $this->setting['port'], SWOOLE_BASE); - $this->http->set($this->setting); - //register_shutdown_function('handleFatal'); - $this->http->on('request', function ($request, $response) - { - if ($request->server['request_uri'] == '/favicon.ico') - { - $response->status(404); - $response->end('Not Found'); - return; - } - $this->getResult($response); - }); - } - - function getResult2($response) - { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $client->on('connect', function ($cli) - { - echo "cli1 connect\n"; - $cli->send("hello world\n"); - }); - - $client->on('Receive', function ($cli, $data) use ($response) - { - echo "cli1 receive\n"; - $response->end($data); - $cli->close(); - }); - - $client->on("error", function ($cli) use ($response) - { - echo "cli1 error\n"; - $response->end("empty\n"); - }); - - $client->on("close", function ($cli) - { - echo "cli1 close\n"; - }); - $client->connect('127.0.0.1', 9501); - } - - function getResult5($response) - { - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($response) - { - $response->header('Content-Type', 'application/json'); - $response->write(json_encode(array($host => $ip))); - $response->end(); - }); - } - - function getResult3($response) - { - $cityId = '01010101'; -// swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) -// { -// if (empty($ip)) -// { -// return $ret; -// } -// else -// { - $ip = '14.18.245.236'; - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - }); -// } -// }); - } - - function getResult($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('test', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $client->close(); - $cityId = '01010101'; - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) - { - if (empty($ip)) - { - return false; - } - else - { - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - - }); - } - }); - }); - }); - } - - function getResult4($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('key', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $response->header('Content-Type', 'application/json'); - $response->end($result); - }); - }); - } - - public function start() - { - $this->init(); - $this->http->start(); - } -} - - -$setting = array( - - 'host' => '127.0.0.1', - 'port' => 9100, - 'worker_num' => 1, - 'dispatch_mode' => 2, - //'reactor_num' => 4, - 'daemonize' => 0, - //'log_file' => './logs/test_udp_server.log', -); - - -$server = new HttpServ(); -$server->set($setting); -$server->start(); diff --git a/examples/websocket/async_client.php b/examples/websocket/async_client.php deleted file mode 100644 index 2d619873dd3..00000000000 --- a/examples/websocket/async_client.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['Trace-Id' => md5(time()),]); -$cli->on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/websocket/client.php b/examples/websocket/client.php index 906b0846219..bd6a03170e3 100644 --- a/examples/websocket/client.php +++ b/examples/websocket/client.php @@ -1,5 +1,5 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_UDP); $server->set([ diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 89d1a4b7879..2398b2b9877 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -10,27 +10,30 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_cxx.h" #include "php_swoole_library.h" #include "php_swoole_process.h" +#include "php_swoole_thread.h" +#include "swoole_iouring.h" -#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) -#include "ext/pcre/php_pcre.h" -#endif +BEGIN_EXTERN_C() #include "zend_exceptions.h" +#include "zend_extensions.h" -BEGIN_EXTERN_C() -#ifdef SW_USE_JSON +#include "ext/pcre/php_pcre.h" #include "ext/json/php_json.h" -#endif + +#include "stubs/php_swoole_arginfo.h" +#include "stubs/php_swoole_ex_arginfo.h" END_EXTERN_C() #include "swoole_mime_type.h" #include "swoole_server.h" #include "swoole_util.h" +#include "swoole_http2.h" #include #include @@ -49,6 +52,9 @@ END_EXTERN_C() #include #include #endif +#ifdef SW_HAVE_ZSTD +#include +#endif #ifdef SW_USE_CARES #include @@ -56,6 +62,9 @@ END_EXTERN_C() using swoole::Server; using swoole::network::Socket; +#ifdef SW_USE_IOURING +using swoole::Iouring; +#endif ZEND_DECLARE_MODULE_GLOBALS(swoole) @@ -70,6 +79,8 @@ static PHP_FUNCTION(swoole_strerror); static PHP_FUNCTION(swoole_clear_error); static PHP_FUNCTION(swoole_errno); static PHP_FUNCTION(swoole_error_log); +static PHP_FUNCTION(swoole_error_log_ex); +static PHP_FUNCTION(swoole_ignore_error); static PHP_FUNCTION(swoole_get_local_ip); static PHP_FUNCTION(swoole_get_local_mac); static PHP_FUNCTION(swoole_hashcode); @@ -80,149 +91,62 @@ static PHP_FUNCTION(swoole_mime_type_get); static PHP_FUNCTION(swoole_mime_type_exists); static PHP_FUNCTION(swoole_mime_type_list); static PHP_FUNCTION(swoole_substr_unserialize); -#ifdef SW_USE_JSON static PHP_FUNCTION(swoole_substr_json_decode); -#endif static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); +static PHP_FUNCTION(swoole_implicit_fn); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_async_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_async_dns_lookup_coro, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_defer, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_socketpair, 0, 0, 3) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, protocol) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_test_kernel_coroutine, 0, 0, 0) - ZEND_ARG_INFO(0, count) - ZEND_ARG_INFO(0, sleep_time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_select, 0, 0, 3) - ZEND_ARG_INFO(1, read_array) - ZEND_ARG_INFO(1, write_array) - ZEND_ARG_INFO(1, error_array) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_substr_unserialize, 0, 0, 2) - ZEND_ARG_INFO(0, str) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_JSON -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_substr_json_decode, 0, 0, 2) - ZEND_ARG_INFO(0, json) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, associative) - ZEND_ARG_INFO(0, depth) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_set_process_name, 0, 0, 1) - ZEND_ARG_INFO(0, process_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_strerror, 0, 0, 1) - ZEND_ARG_INFO(0, errno) - ZEND_ARG_INFO(0, error_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_error_log, 0, 0, 2) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, msg) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_hashcode, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -/* add/set */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_write, 0, 0, 2) - ZEND_ARG_INFO(0, suffix) - ZEND_ARG_INFO(0, mime_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_delete, 0, 0, 1) - ZEND_ARG_INFO(0, suffix) -ZEND_END_ARG_INFO() - -/* get/exists */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_read, 0, 0, 1) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_functions[] = { - PHP_FE(swoole_version, arginfo_swoole_void) - PHP_FE(swoole_cpu_num, arginfo_swoole_void) - PHP_FE(swoole_last_error, arginfo_swoole_void) + PHP_FE(swoole_version, arginfo_swoole_version) + PHP_FE(swoole_cpu_num, arginfo_swoole_cpu_num) + PHP_FE(swoole_last_error, arginfo_swoole_last_error) /*------swoole_async_io------*/ PHP_FE(swoole_async_dns_lookup_coro, arginfo_swoole_async_dns_lookup_coro) - PHP_FE(swoole_async_set, arginfo_swoole_async_set) + PHP_FE(swoole_async_set, arginfo_swoole_async_set) /*------swoole_coroutine------*/ - PHP_FE(swoole_coroutine_create, arginfo_swoole_coroutine_create) - PHP_FE(swoole_coroutine_defer, arginfo_swoole_coroutine_defer) - PHP_FE(swoole_coroutine_socketpair, arginfo_swoole_coroutine_socketpair) + PHP_FE(swoole_coroutine_create, arginfo_swoole_coroutine_create) + PHP_FE(swoole_coroutine_defer, arginfo_swoole_coroutine_defer) + PHP_FE(swoole_coroutine_socketpair, arginfo_swoole_coroutine_socketpair) PHP_FE(swoole_test_kernel_coroutine, arginfo_swoole_test_kernel_coroutine) /*------other-----*/ - PHP_FE(swoole_client_select, arginfo_swoole_client_select) - PHP_FALIAS(swoole_select, swoole_client_select, arginfo_swoole_client_select) - PHP_FE(swoole_set_process_name, arginfo_swoole_set_process_name) - PHP_FE(swoole_get_local_ip, arginfo_swoole_void) - PHP_FE(swoole_get_local_mac, arginfo_swoole_void) - PHP_FE(swoole_strerror, arginfo_swoole_strerror) - PHP_FE(swoole_errno, arginfo_swoole_void) - PHP_FE(swoole_clear_error, arginfo_swoole_void) - PHP_FE(swoole_error_log, arginfo_swoole_error_log) - PHP_FE(swoole_hashcode, arginfo_swoole_hashcode) - PHP_FE(swoole_mime_type_add, arginfo_swoole_mime_type_write) - PHP_FE(swoole_mime_type_set, arginfo_swoole_mime_type_write) - PHP_FE(swoole_mime_type_delete, arginfo_swoole_mime_type_delete) - PHP_FE(swoole_mime_type_get, arginfo_swoole_mime_type_read) - PHP_FALIAS(swoole_get_mime_type, swoole_mime_type_get, arginfo_swoole_mime_type_read) - PHP_FE(swoole_mime_type_exists, arginfo_swoole_mime_type_read) - PHP_FE(swoole_mime_type_list, arginfo_swoole_void) - PHP_FE(swoole_clear_dns_cache, arginfo_swoole_void) + PHP_FE(swoole_client_select, arginfo_swoole_client_select) + PHP_FALIAS(swoole_select, swoole_client_select, arginfo_swoole_client_select) + PHP_FE(swoole_set_process_name, arginfo_swoole_set_process_name) + PHP_FE(swoole_get_local_ip, arginfo_swoole_get_local_ip) + PHP_FE(swoole_get_local_mac, arginfo_swoole_get_local_mac) + PHP_FE(swoole_strerror, arginfo_swoole_strerror) + PHP_FE(swoole_errno, arginfo_swoole_errno) + PHP_FE(swoole_clear_error, arginfo_swoole_clear_error) + PHP_FE(swoole_error_log, arginfo_swoole_error_log) + PHP_FE(swoole_error_log_ex, arginfo_swoole_error_log_ex) + PHP_FE(swoole_ignore_error, arginfo_swoole_ignore_error) + PHP_FE(swoole_hashcode, arginfo_swoole_hashcode) + PHP_FE(swoole_mime_type_add, arginfo_swoole_mime_type_add) + PHP_FE(swoole_mime_type_set, arginfo_swoole_mime_type_set) + PHP_FE(swoole_mime_type_delete, arginfo_swoole_mime_type_delete) + PHP_FE(swoole_mime_type_get, arginfo_swoole_mime_type_get) + PHP_FALIAS(swoole_get_mime_type, swoole_mime_type_get, arginfo_swoole_mime_type_get) + PHP_FE(swoole_mime_type_exists, arginfo_swoole_mime_type_exists) + PHP_FE(swoole_mime_type_list, arginfo_swoole_mime_type_list) + PHP_FE(swoole_clear_dns_cache, arginfo_swoole_clear_dns_cache) PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize) -#ifdef SW_USE_JSON PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) -#endif - PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_void) + PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) + // for test + PHP_FE(swoole_implicit_fn, arginfo_swoole_implicit_fn) + // for admin server + ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) + ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) + ZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle) + ZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup) + ZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add) + ZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove) PHP_FE_END /* Must be the last line in swoole_functions[] */ }; static const zend_module_dep swoole_deps[] = { -#ifdef SW_USE_JSON ZEND_MOD_REQUIRED("json") -#endif #ifdef SW_USE_MYSQLND ZEND_MOD_REQUIRED("mysqlnd") #endif @@ -232,6 +156,10 @@ static const zend_module_dep swoole_deps[] = { #ifdef SW_USE_CURL ZEND_MOD_REQUIRED("curl") #endif +#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) || defined(SW_USE_SQLITE) + ZEND_MOD_REQUIRED("pdo") +#endif + ZEND_MOD_END }; @@ -258,6 +186,9 @@ zend_class_entry *swoole_error_ce; zend_object_handlers swoole_error_handlers; #ifdef COMPILE_DL_SWOOLE +#ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +#endif ZEND_GET_MODULE(swoole) #endif @@ -269,8 +200,8 @@ PHP_INI_BEGIN() /** * enable swoole coroutine */ -STD_ZEND_INI_BOOLEAN("swoole.enable_coroutine", "On", PHP_INI_ALL, OnUpdateBool, enable_coroutine, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_library", "On", PHP_INI_ALL, OnUpdateBool, enable_library, zend_swoole_globals, swoole_globals) +STD_ZEND_INI_BOOLEAN("swoole.enable_fiber_mock", "Off", PHP_INI_ALL, OnUpdateBool, enable_fiber_mock, zend_swoole_globals, swoole_globals) /** * enable swoole coroutine epreemptive scheduler */ @@ -291,12 +222,17 @@ PHP_INI_END() // clang-format on static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { - swoole_globals->enable_coroutine = 1; swoole_globals->enable_library = 1; + swoole_globals->enable_fiber_mock = 0; swoole_globals->enable_preemptive_scheduler = 0; swoole_globals->socket_buffer_size = SW_SOCKET_BUFFER_SIZE; swoole_globals->display_errors = 1; swoole_globals->use_shortname = 1; + swoole_globals->in_autoload = nullptr; + if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || + strcmp("embed", sapi_module.name) == 0 || strcmp("micro", sapi_module.name) == 0) { + swoole_globals->cli = 1; + } } void php_swoole_register_shutdown_function(const char *function) { @@ -309,15 +245,9 @@ void php_swoole_register_shutdown_function(const char *function) { register_user_shutdown_function(Z_STRVAL(function_name), Z_STRLEN(function_name), &shutdown_function_entry); #else zval *function_name; -#if PHP_VERSION_ID >= 80000 shutdown_function_entry.arg_count = 0; shutdown_function_entry.arguments = NULL; function_name = &shutdown_function_entry.function_name; -#else - shutdown_function_entry.arg_count = 1; - shutdown_function_entry.arguments = (zval *) safe_emalloc(sizeof(zval), 1, 0); - function_name = &shutdown_function_entry.arguments[0]; -#endif ZVAL_STRING(function_name, function); register_user_shutdown_function(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), &shutdown_function_entry); #endif @@ -328,17 +258,19 @@ void php_swoole_set_global_option(HashTable *vht) { #ifdef SW_DEBUG if (php_swoole_array_get_value(vht, "debug_mode", ztmp) && zval_is_true(ztmp)) { - sw_logger()->set_level(0); + swoole_set_log_level(0); } #endif + // [Logger] + // ====================================================================== if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) { - SwooleG.trace_flags = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); + swoole_set_trace_flags(zval_get_long(ztmp)); } if (php_swoole_array_get_value(vht, "log_file", ztmp)) { - sw_logger()->open(zend::String(ztmp).val()); + swoole_set_log_file(zend::String(ztmp).val()); } if (php_swoole_array_get_value(vht, "log_level", ztmp)) { - sw_logger()->set_level(zval_get_long(ztmp)); + swoole_set_log_level(zval_get_long(ztmp)); } if (php_swoole_array_get_value(vht, "log_date_format", ztmp)) { sw_logger()->set_date_format(zend::String(ztmp).val()); @@ -352,10 +284,13 @@ void php_swoole_set_global_option(HashTable *vht) { if (php_swoole_array_get_value(vht, "display_errors", ztmp)) { SWOOLE_G(display_errors) = zval_is_true(ztmp); } + // [DNS] + // ====================================================================== if (php_swoole_array_get_value(vht, "dns_server", ztmp)) { swoole_set_dns_server(zend::String(ztmp).to_std_string()); } - + // [Socket] + // ====================================================================== auto timeout_format = [](zval *v) -> double { double timeout = zval_get_double(v); if (timeout <= 0 || timeout > INT_MAX) { @@ -364,7 +299,6 @@ void php_swoole_set_global_option(HashTable *vht) { return timeout; } }; - if (php_swoole_array_get_value(vht, "socket_dns_timeout", ztmp)) { Socket::default_dns_timeout = timeout_format(ztmp); } @@ -380,13 +314,30 @@ void php_swoole_set_global_option(HashTable *vht) { Socket::default_read_timeout = timeout_format(ztmp); } if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - Socket::default_buffer_size = zval_get_long(ztmp); + Socket::default_buffer_size = php_swoole_parse_to_size(ztmp); } if (php_swoole_array_get_value(vht, "socket_timeout", ztmp)) { Socket::default_read_timeout = Socket::default_write_timeout = timeout_format(ztmp); } - if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { - SwooleG.max_concurrency = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); + // [HTTP2] + // ====================================================================== + if (php_swoole_array_get_value(vht, "http2_header_table_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, php_swoole_parse_to_size(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_enable_push", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_concurrent_streams", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_init_window_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, php_swoole_parse_to_size(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_frame_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, php_swoole_parse_to_size(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_header_list_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, php_swoole_parse_to_size(ztmp)); } } @@ -398,10 +349,61 @@ SW_API bool php_swoole_is_enable_coroutine() { if (sw_server()) { return sw_server()->is_enable_coroutine(); } else { - return SWOOLE_G(enable_coroutine); + return SwooleG.enable_coroutine; } } +SW_API zend_long php_swoole_parse_to_size(zval *zv) { + if (ZVAL_IS_STRING(zv)) { +#if PHP_VERSION_ID >= 80200 + zend_string *errstr; + auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr); + if (errstr) { + php_swoole_fatal_error( + E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + return size; +#else + return zend_atol(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); +#endif + } else { + return zval_get_long(zv); + } +} + +SW_API zend_string *php_swoole_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + zend_string *result = nullptr; + if (!EG(exception)) { + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); + } + smart_str_free(&serialized_data); + return result; +} + +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - ZSTR_VAL(data)), + l); + } + return unserialized; +} + static void fatal_error(int code, const char *format, ...) { va_list args; va_start(args, format); @@ -409,13 +411,14 @@ static void fatal_error(int code, const char *format, ...) { zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); va_end(args); - zend_try { - zend_exception_error(exception, E_ERROR); - } - zend_catch { - exit(255); + zend::print_error(exception, E_ERROR); + +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + php_swoole_thread_bailout(); } - zend_end_try(); +#endif + swoole_exit(255); } static void bug_report_message_init() { @@ -451,10 +454,7 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_HAVE_BROTLI SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_BROTLI", 1); #endif -#ifdef SW_USE_HTTP2 SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_HTTP2", 1); -#endif - SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_SHORTNAME", SWOOLE_G(use_shortname)); /** @@ -576,6 +576,11 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OPERATION_NOT_SUPPORT", SW_ERROR_OPERATION_NOT_SUPPORT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PROTOCOL_ERROR", SW_ERROR_PROTOCOL_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WRONG_OPERATION", SW_ERROR_WRONG_OPERATION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_RUNTIME_NOTICE", SW_ERROR_PHP_RUNTIME_NOTICE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FOR_TEST", SW_ERROR_FOR_TEST); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NO_PAYLOAD", SW_ERROR_NO_PAYLOAD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_UNDEFINED_BEHAVIOR", SW_ERROR_UNDEFINED_BEHAVIOR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NOT_THREAD_SAFETY", SW_ERROR_NOT_THREAD_SAFETY); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_NOT_EXIST", SW_ERROR_FILE_NOT_EXIST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_TOO_LARGE", SW_ERROR_FILE_TOO_LARGE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_EMPTY", SW_ERROR_FILE_EMPTY); @@ -586,6 +591,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_NO_SERVER", SW_ERROR_DNSLOOKUP_NO_SERVER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_BAD_IPV6_ADDRESS", SW_ERROR_BAD_IPV6_ADDRESS); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_UNREGISTERED_SIGNAL", SW_ERROR_UNREGISTERED_SIGNAL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_BAD_HOST_ADDR", SW_ERROR_BAD_HOST_ADDR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_EVENT_SOCKET_REMOVED", SW_ERROR_EVENT_SOCKET_REMOVED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER", SW_ERROR_SESSION_CLOSED_BY_SERVER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT", SW_ERROR_SESSION_CLOSED_BY_CLIENT); @@ -608,6 +614,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE", SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND", SW_ERROR_PACKAGE_LENGTH_NOT_FOUND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE", SW_ERROR_DATA_LENGTH_TOO_LARGE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_MALFORMED_DATA", SW_ERROR_PACKAGE_MALFORMED_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG", SW_ERROR_TASK_PACKAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_DISPATCH_FAIL", SW_ERROR_TASK_DISPATCH_FAIL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_TIMEOUT", SW_ERROR_TASK_TIMEOUT); @@ -615,6 +622,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER", SW_ERROR_HTTP2_STREAM_NO_HEADER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND", SW_ERROR_HTTP2_STREAM_NOT_FOUND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_IGNORE", SW_ERROR_HTTP2_STREAM_IGNORE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED", SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_BAD_REQUEST", SW_ERROR_AIO_BAD_REQUEST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_CANCELED", SW_ERROR_AIO_CANCELED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_TIMEOUT", SW_ERROR_AIO_TIMEOUT); @@ -630,11 +638,16 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_INVALID_PROTOCOL", SW_ERROR_HTTP_INVALID_PROTOCOL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED", SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE", SW_ERROR_HTTP_PROXY_BAD_RESPONSE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONFLICT_HEADER", SW_ERROR_HTTP_CONFLICT_HEADER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONTEXT_UNAVAILABLE", SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_COOKIE_UNAVAILABLE", SW_ERROR_HTTP_COOKIE_UNAVAILABLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT", SW_ERROR_WEBSOCKET_BAD_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE", SW_ERROR_WEBSOCKET_BAD_OPCODE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNCONNECTED", SW_ERROR_WEBSOCKET_UNCONNECTED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED", SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_PACK_FAILED", SW_ERROR_WEBSOCKET_PACK_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNPACK_FAILED", SW_ERROR_WEBSOCKET_UNPACK_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_INCOMPLETE_PACKET", SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT", SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET", SW_ERROR_SERVER_TOO_MANY_SOCKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_TERMINATED", SW_ERROR_SERVER_WORKER_TERMINATED); @@ -647,6 +660,10 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_REQUEST", SW_ERROR_SERVER_INVALID_REQUEST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_CONNECT_FAIL", SW_ERROR_SERVER_CONNECT_FAIL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_COMMAND", SW_ERROR_SERVER_INVALID_COMMAND); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE", SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT", SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_CALLBACK", SW_ERROR_SERVER_INVALID_CALLBACK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_UNRELATED_THREAD", SW_ERROR_SERVER_UNRELATED_THREAD); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA", SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA", SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA); @@ -668,6 +685,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_NOT_EXISTS", SW_ERROR_CO_NOT_EXISTS); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_CANCELED", SW_ERROR_CO_CANCELED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_TIMEDOUT", SW_ERROR_CO_TIMEDOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT", SW_ERROR_CO_SOCKET_CLOSE_WAIT); /** * trace log @@ -702,6 +720,11 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_TABLE", SW_TRACE_TABLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_CURL", SW_TRACE_CO_CURL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CARES", SW_TRACE_CARES); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ZLIB", SW_TRACE_ZLIB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_PGSQL", SW_TRACE_CO_PGSQL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ODBC", SW_TRACE_CO_ODBC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ORACLE", SW_TRACE_CO_ORACLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_SQLITE", SW_TRACE_CO_SQLITE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** @@ -730,31 +753,33 @@ PHP_MINIT_FUNCTION(swoole) { */ SW_REGISTER_LONG_CONSTANT("SWOOLE_IOV_MAX", IOV_MAX); + /** + * iouring + */ +#ifdef SW_USE_IOURING + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_DEFAULT", SW_IOURING_DEFAULT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_SQPOLL", SW_IOURING_SQPOLL); +#endif + // clang-format on if (SWOOLE_G(use_shortname)) { - SW_FUNCTION_ALIAS(CG(function_table), "swoole_coroutine_create", CG(function_table), "go"); - SW_FUNCTION_ALIAS(CG(function_table), "swoole_coroutine_defer", CG(function_table), "defer"); + SW_FUNCTION_ALIAS( + CG(function_table), "swoole_coroutine_create", CG(function_table), "go", arginfo_swoole_coroutine_create); + SW_FUNCTION_ALIAS( + CG(function_table), "swoole_coroutine_defer", CG(function_table), "defer", arginfo_swoole_coroutine_defer); } swoole_init(); // init bug report message bug_report_message_init(); - if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0) { - SWOOLE_G(cli) = 1; - } - SW_INIT_CLASS_ENTRY_EX2(swoole_exception, - "Swoole\\Exception", - "swoole_exception", - nullptr, - nullptr, - zend_ce_exception, - zend_get_std_object_handlers()); + SW_INIT_CLASS_ENTRY_EX2( + swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); SW_INIT_CLASS_ENTRY_EX2( - swoole_error, "Swoole\\Error", "swoole_error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); + swoole_error, "Swoole\\Error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); /** **/ php_swoole_event_minit(module_number); @@ -769,35 +794,55 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_coroutine_minit(module_number); php_swoole_coroutine_system_minit(module_number); php_swoole_coroutine_scheduler_minit(module_number); + php_swoole_coroutine_lock_minit(module_number); php_swoole_channel_coro_minit(module_number); php_swoole_runtime_minit(module_number); // client php_swoole_socket_coro_minit(module_number); php_swoole_client_minit(module_number); + php_swoole_client_async_minit(module_number); php_swoole_client_coro_minit(module_number); php_swoole_http_client_coro_minit(module_number); - php_swoole_mysql_coro_minit(module_number); - php_swoole_redis_coro_minit(module_number); -#ifdef SW_USE_HTTP2 php_swoole_http2_client_coro_minit(module_number); -#endif // server php_swoole_server_minit(module_number); php_swoole_server_port_minit(module_number); php_swoole_http_request_minit(module_number); php_swoole_http_response_minit(module_number); + php_swoole_http_cookie_minit(module_number); php_swoole_http_server_minit(module_number); php_swoole_http_server_coro_minit(module_number); php_swoole_websocket_server_minit(module_number); php_swoole_redis_server_minit(module_number); + php_swoole_name_resolver_minit(module_number); +#ifdef SW_USE_PGSQL + php_swoole_pgsql_minit(module_number); +#endif +#ifdef SW_USE_ODBC + php_swoole_odbc_minit(module_number); +#endif +#ifdef SW_USE_ORACLE + php_swoole_oracle_minit(module_number); +#endif +#ifdef SW_USE_SQLITE + php_swoole_sqlite_minit(module_number); +#endif +#ifdef SW_THREAD + php_swoole_thread_minit(module_number); + php_swoole_thread_atomic_minit(module_number); + php_swoole_thread_lock_minit(module_number); + php_swoole_thread_barrier_minit(module_number); + php_swoole_thread_queue_minit(module_number); + php_swoole_thread_map_minit(module_number); + php_swoole_thread_arraylist_minit(module_number); +#endif SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); SwooleG.dns_cache_refresh_time = 60; // enable pcre.jit and use swoole extension on MacOS will lead to coredump, disable it temporarily -#if defined(PHP_PCRE_VERSION) && defined(HAVE_PCRE_JIT_SUPPORT) && PHP_VERSION_ID >= 70300 && __MACH__ && \ - !defined(SW_DEBUG) +#if defined(PHP_PCRE_VERSION) && defined(HAVE_PCRE_JIT_SUPPORT) && __MACH__ && !defined(SW_DEBUG) PCRE_G(jit) = 0; #endif @@ -810,9 +855,23 @@ PHP_MINIT_FUNCTION(swoole) { /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(swoole) { - swoole_clean(); zend::known_strings_dtor(); + php_swoole_runtime_mshutdown(); + php_swoole_websocket_server_mshutdown(); +#ifdef SW_USE_PGSQL + php_swoole_pgsql_mshutdown(); +#endif + +#ifdef SW_USE_ORACLE + php_swoole_oracle_mshutdown(); +#endif + +#ifdef SW_USE_SQLITE + php_swoole_sqlite_mshutdown(); +#endif + + swoole_clean(); return SUCCESS; } @@ -850,6 +909,9 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef HAVE_KQUEUE php_info_print_table_row(2, "kqueue", "enabled"); #endif +#ifdef SW_THREAD + php_info_print_table_row(2, "thread", "enabled"); +#endif #ifdef HAVE_SIGNALFD php_info_print_table_row(2, "signalfd", "enabled"); #endif @@ -878,18 +940,11 @@ PHP_MINFO_FUNCTION(swoole) { php_info_print_table_row(2, "dtls", "enabled"); #endif #endif -#ifdef SW_USE_HTTP2 php_info_print_table_row(2, "http2", "enabled"); -#endif -#ifdef SW_USE_JSON php_info_print_table_row(2, "json", "enabled"); -#endif #ifdef SW_USE_CURL php_info_print_table_row(2, "curl-native", "enabled"); #endif -#ifdef HAVE_PCRE - php_info_print_table_row(2, "pcre", "enabled"); -#endif #ifdef SW_USE_CARES php_info_print_table_row(2, "c-ares", ares_version(nullptr)); #endif @@ -904,6 +959,13 @@ PHP_MINFO_FUNCTION(swoole) { snprintf(buf, sizeof(buf), "E%u/D%u", BrotliEncoderVersion(), BrotliDecoderVersion()); php_info_print_table_row(2, "brotli", buf); #endif +#ifdef SW_HAVE_ZSTD +#ifdef ZSTD_VERSION_NUMBER + php_info_print_table_row(2, "zstd", ZSTD_VERSION_STRING); +#else + php_info_print_table_row(2, "zstd", "enabled"); +#endif +#endif #ifdef HAVE_MUTEX_TIMEDLOCK php_info_print_table_row(2, "mutex_timedlock", "enabled"); #endif @@ -922,9 +984,20 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_TCMALLOC php_info_print_table_row(2, "tcmalloc", "enabled"); #endif - php_info_print_table_row(2, "async_redis", "enabled"); -#ifdef SW_USE_POSTGRESQL - php_info_print_table_row(2, "coroutine_postgresql", "enabled"); +#ifdef SW_USE_PGSQL + php_info_print_table_row(2, "coroutine_pgsql", "enabled"); +#endif +#ifdef SW_USE_ODBC + php_info_print_table_row(2, "coroutine_odbc", "enabled"); +#endif +#ifdef SW_USE_ORACLE + php_info_print_table_row(2, "coroutine_oracle", "enabled"); +#endif +#ifdef SW_USE_SQLITE + php_info_print_table_row(2, "coroutine_sqlite", "enabled"); +#endif +#ifdef SW_USE_IOURING + php_info_print_table_row(2, "io_uring", "enabled"); #endif php_info_print_table_end(); @@ -998,13 +1071,55 @@ const swoole::Allocator *sw_zend_string_allocator() { return &zend_string_allocator; } +void sw_php_exit(int status) { + EG(exit_status) = status; +#ifdef SW_THREAD + php_swoole_thread_bailout(); +#else + zend_bailout(); +#endif +} + +bool sw_zval_is_serializable(zval *struc) { +again: + switch (Z_TYPE_P(struc)) { + case IS_OBJECT: { + if (Z_OBJCE_P(struc)->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) { + return false; + } + break; + } + case IS_ARRAY: { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(struc), elem) { + if (!sw_zval_is_serializable(elem)) { + return false; + } + } + ZEND_HASH_FOREACH_END(); + break; + } + case IS_REFERENCE: + struc = Z_REFVAL_P(struc); + goto again; + default: + break; + } + return true; +} + +static void sw_after_fork(void *args) { +#ifdef ZEND_MAX_EXECUTION_TIMERS + zend_max_execution_timer_init(); +#endif +} + PHP_RINIT_FUNCTION(swoole) { if (!SWOOLE_G(cli)) { return SUCCESS; } SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_BEGIN; - SwooleG.running = 1; php_swoole_register_shutdown_function("swoole_internal_call_user_shutdown_begin"); @@ -1014,7 +1129,24 @@ PHP_RINIT_FUNCTION(swoole) { && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) #endif ) { + // https://github.com/swoole/swoole-src/issues/5182 + /** + * xdebug will hook zend_execute_ex to xdebug_execute_ex. + * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced + * with the execute_ex function. + */ + void (*old_zend_execute_ex)(zend_execute_data * execute_data) = nullptr; + if (UNEXPECTED(zend_execute_ex != execute_ex)) { + old_zend_execute_ex = zend_execute_ex; + zend_execute_ex = execute_ex; + } + php_swoole_load_library(); + + if (UNEXPECTED(old_zend_execute_ex)) { + zend_execute_ex = old_zend_execute_ex; + old_zend_execute_ex = nullptr; + } } #ifdef ZEND_SIGNALS @@ -1022,8 +1154,18 @@ PHP_RINIT_FUNCTION(swoole) { SIGG(check) = 0; #endif + swoole_add_hook(SW_GLOBAL_HOOK_AFTER_FORK, sw_after_fork, 0); + + php_swoole_http_server_rinit(); + php_swoole_websocket_server_rinit(); php_swoole_coroutine_rinit(); php_swoole_runtime_rinit(); +#ifdef SW_USE_ORACLE + php_swoole_oracle_rinit(); +#endif +#ifdef SW_THREAD + php_swoole_thread_rinit(); +#endif SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END; @@ -1039,17 +1181,21 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { rshutdown_callbacks.execute(); - swoole_event_free(); - php_swoole_server_rshutdown(); + php_swoole_http_server_rshutdown(); + php_swoole_websocket_server_rshutdown(); php_swoole_async_coro_rshutdown(); php_swoole_redis_server_rshutdown(); php_swoole_coroutine_rshutdown(); + php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); + php_swoole_process_rshutdown(); +#ifdef SW_THREAD + php_swoole_thread_rshutdown(); +#endif - php_swoole_process_clean(); + swoole_event_free(); - SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; #ifdef PHP_STREAM_FLAG_NO_CLOSE @@ -1061,8 +1207,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { if (!zstream) { return; } - stream = - (php_stream *) zend_fetch_resource2_ex((zstream), "stream", php_file_le_stream(), php_file_le_pstream()); + stream = (php_stream *) zend_fetch_resource2_ex((zstream), NULL, php_file_le_stream(), php_file_le_pstream()); if (!stream) { return; } @@ -1154,7 +1299,7 @@ static PHP_FUNCTION(swoole_strerror) { static PHP_FUNCTION(swoole_error_log) { char *msg; size_t l_msg; - zend_long level = 0; + zend_long level; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(level) @@ -1164,6 +1309,30 @@ static PHP_FUNCTION(swoole_error_log) { sw_logger()->put(level, msg, l_msg); } +static PHP_FUNCTION(swoole_error_log_ex) { + char *msg; + size_t l_msg; + zend_long level, error; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(level) + Z_PARAM_LONG(error) + Z_PARAM_STRING(msg, l_msg) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_error_log(level, (int) error, "%.*s", (int) l_msg, msg); +} + +static PHP_FUNCTION(swoole_ignore_error) { + zend_long error; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(error) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_ignore_error(error); +} + static PHP_FUNCTION(swoole_mime_type_add) { zend_string *suffix; zend_string *mime_type; @@ -1362,17 +1531,19 @@ static PHP_FUNCTION(swoole_substr_unserialize) { } if (offset < 0) { offset = buf_len + offset; + if (offset < 0) { + RETURN_FALSE; + } } if ((zend_long) buf_len <= offset) { RETURN_FALSE; } - if (length <= 0) { + if (length <= 0 || length > (zend_long) (buf_len - offset)) { length = buf_len - offset; } zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); } -#ifdef SW_USE_JSON static PHP_FUNCTION(swoole_substr_json_decode) { zend_long offset, length = 0; char *str; @@ -1393,15 +1564,21 @@ static PHP_FUNCTION(swoole_substr_json_decode) { ZEND_PARSE_PARAMETERS_END(); if (str_len == 0) { - RETURN_FALSE; + php_error_docref(nullptr, E_WARNING, "Non-empty string required"); + RETURN_NULL(); } if (offset < 0) { offset = str_len + offset; + if (offset < 0) { + php_error_docref(nullptr, E_WARNING, "Offset must be not less than the negative length of the string"); + RETURN_NULL(); + } } if ((zend_long) str_len <= offset) { - RETURN_FALSE; + php_error_docref(nullptr, E_WARNING, "Offset must be less than the length of the string"); + RETURN_NULL(); } - if (length <= 0) { + if (length <= 0 || length > (zend_long) (str_len - offset)) { length = str_len - offset; } /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ @@ -1414,4 +1591,40 @@ static PHP_FUNCTION(swoole_substr_json_decode) { } zend::json_decode(return_value, str + offset, length, options, depth); } -#endif + +/** + * The implicit functions are intended solely for internal testing and will not be documented. + * These functions are unsafe, do not use if you are not an internal developer. + */ +static PHP_FUNCTION(swoole_implicit_fn) { + char *fn; + size_t l_fn; + zval *zargs = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(fn, l_fn) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zargs) + ZEND_PARSE_PARAMETERS_END(); + + if (SW_STRCASEEQ(fn, l_fn, "fatal_error")) { + swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); + php_printf("never be executed here\n"); + } else if (SW_STRCASEEQ(fn, l_fn, "bailout")) { + sw_php_exit(zargs ? zval_get_long(zargs) : 95); + } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { + abort(); + } else if (SW_STRCASEEQ(fn, l_fn, "refcount")) { + RETURN_LONG(zval_refcount_p(zargs)); + } else if (SW_STRCASEEQ(fn, l_fn, "func_handler")) { + auto fn = zval_get_string(zargs); + zend_function *zf = (zend_function *) zend_hash_find_ptr(EG(function_table), fn); + zend_string_release(fn); + if (zf == nullptr) { + RETURN_FALSE; + } + printf("zif_handler=%p\n", zf->internal_function.handler); + } else { + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "unknown fn '%s'", fn); + } +} diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h new file mode 100644 index 00000000000..14d1555e803 --- /dev/null +++ b/ext-src/php_swoole_call_stack.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Yurun | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole.h" + +#ifdef ZEND_CHECK_STACK_LIMIT +#define HOOK_PHP_CALL_STACK(callback) \ + void *__stack_limit = EG(stack_limit); \ + void *__stack_base = EG(stack_base); \ + EG(stack_base) = (void *) 0; \ + EG(stack_limit) = (void *) 0; \ + callback EG(stack_limit) = __stack_limit; \ + EG(stack_base) = __stack_base; +#else +#define HOOK_PHP_CALL_STACK(callback) callback +#endif diff --git a/ext-src/php_swoole_client.h b/ext-src/php_swoole_client.h index be9a3c7e8d4..f899db070ee 100644 --- a/ext-src/php_swoole_client.h +++ b/ext-src/php_swoole_client.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -21,7 +21,45 @@ #include "php_swoole_cxx.h" #include "swoole_client.h" +struct AsyncClientObject { + zend::Callable *onConnect; + zend::Callable *onReceive; + zend::Callable *onClose; + zend::Callable *onError; + zend::Callable *onBufferFull; + zend::Callable *onBufferEmpty; +#ifdef SW_USE_OPENSSL + zend::Callable *onSSLReady; +#endif + zval _zobject; +}; + +struct ClientObject { + swoole::network::Client *cli; +#ifdef SWOOLE_SOCKETS_SUPPORT + zval *zsocket; +#endif + AsyncClientObject *async; + zend_object std; +}; + +static sw_inline ClientObject *php_swoole_client_fetch_object(zend_object *obj) { + return (ClientObject *) ((char *) obj - swoole_client_handlers.offset); +} + +static sw_inline ClientObject *php_swoole_client_fetch_object(zval *zobj) { + return php_swoole_client_fetch_object(Z_OBJ_P(zobj)); +} + +static sw_inline swoole::network::Client *php_swoole_client_get_cli(zval *zobject) { + return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli; +} + +swoole::network::Client *php_swoole_client_get_cli_safe(zval *zobject); +void php_swoole_client_free(zval *zobject, swoole::network::Client *cli); +void php_swoole_client_async_free_object(ClientObject *client_obj); bool php_swoole_client_check_setting(swoole::network::Client *cli, zval *zset); #ifdef SW_USE_OPENSSL void php_swoole_client_check_ssl_setting(swoole::network::Client *cli, zval *zset); +bool php_swoole_client_enable_ssl_encryption(swoole::network::Client *cli, zval *zobject); #endif diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 62ce9fd075a..383aefef197 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -25,6 +25,12 @@ #include "zend_vm.h" #include "zend_closures.h" +#if PHP_VERSION_ID >= 80100 +#define SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT 1 +#include "zend_fibers.h" +#include "zend_observer.h" +#endif + #include #include @@ -53,9 +59,7 @@ struct PHPContext { zend_vm_stack vm_stack; size_t vm_stack_page_size; zend_execute_data *execute_data; -#if PHP_VERSION_ID >= 80000 uint32_t jit_trace_num; -#endif zend_error_handling_t error_handling; zend_class_entry *exception_class; zend_object *exception; @@ -70,6 +74,17 @@ struct PHPContext { int ori_error_reporting; int tmp_error_reporting; Coroutine *co; + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + zval return_value; +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + zend_fiber_context *fiber_context; + bool fiber_init_notified; +#endif +#ifdef ZEND_CHECK_STACK_LIMIT + void *stack_base; + void *stack_limit; +#endif std::stack *defer_tasks; SwapCallback *on_yield; SwapCallback *on_resume; @@ -85,6 +100,7 @@ class PHPCoroutine { zend_fcall_info_cache *fci_cache; zval *argv; uint32_t argc; + zval *callable; }; struct Config { @@ -94,7 +110,7 @@ class PHPCoroutine { bool enable_deadlock_check; }; - static zend_array *options; + static SW_THREAD_LOCAL zend_array *options; enum HookType { HOOK_NONE = 0, @@ -113,6 +129,10 @@ class PHPCoroutine { HOOK_BLOCKING_FUNCTION = 1u << 13, HOOK_SOCKETS = 1u << 14, HOOK_STDIO = 1u << 15, + HOOK_PDO_PGSQL = 1u << 16, + HOOK_PDO_ODBC = 1u << 17, + HOOK_PDO_ORACLE = 1u << 18, + HOOK_PDO_SQLITE = 1u << 19, #ifdef SW_USE_CURL HOOK_ALL = 0x7fffffff ^ HOOK_CURL, #else @@ -121,16 +141,15 @@ class PHPCoroutine { }; static const uint8_t MAX_EXEC_MSEC = 10; - static void init(); static void shutdown(); - static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv); + static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable); + static PHPContext *create_context(Args *args); static void defer(zend::Function *fci); static void deadlock_check(); static bool enable_hook(uint32_t flags); static bool disable_hook(); static void disable_unsafe_function(); static void enable_unsafe_function(); - static void interrupt_thread_stop(); static inline long get_cid() { @@ -138,8 +157,8 @@ class PHPCoroutine { } static inline long get_pcid(long cid = 0) { - PHPContext *task = cid == 0 ? get_context() : get_context_by_cid(cid); - return sw_likely(task) ? task->pcid : 0; + PHPContext *ctx = cid == 0 ? get_context() : get_context_by_cid(cid); + return sw_likely(ctx) ? ctx->pcid : 0; } static inline long get_elapsed(long cid = 0) { @@ -147,17 +166,37 @@ class PHPCoroutine { } static inline PHPContext *get_context() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - return task ? task : &main_task; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + return ctx ? ctx : &main_context; } - static inline PHPContext *get_origin_context(PHPContext *task) { - Coroutine *co = task->co->get_origin(); - return co ? (PHPContext *) co->get_task() : &main_task; + static inline PHPContext *get_origin_context(PHPContext *ctx) { + Coroutine *co = ctx->co->get_origin(); + return co ? (PHPContext *) co->get_task() : &main_context; } static inline PHPContext *get_context_by_cid(long cid) { - return cid == -1 ? &main_task : (PHPContext *) Coroutine::get_task_by_cid(cid); + return cid == -1 ? &main_context : (PHPContext *) Coroutine::get_task_by_cid(cid); + } + + static inline ssize_t get_stack_usage(long cid) { + zend_long current_cid = PHPCoroutine::get_cid(); + if (cid == 0) { + cid = current_cid; + } + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + return -1; + } + zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : ctx->vm_stack; + size_t usage = 0; + + while (stack) { + usage += (stack->end - stack->top) * sizeof(zval); + stack = stack->prev; + } + return usage; } static inline uint64_t get_max_num() { @@ -172,23 +211,23 @@ class PHPCoroutine { config.enable_deadlock_check = value; } - static inline bool is_schedulable(PHPContext *task) { - return task->enable_scheduler && (Timer::get_absolute_msec() - task->last_msec > MAX_EXEC_MSEC); + static inline bool is_schedulable(PHPContext *ctx) { + return ctx->enable_scheduler && (Timer::get_absolute_msec() - ctx->last_msec > MAX_EXEC_MSEC); } static inline bool enable_scheduler() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - if (task && !task->enable_scheduler) { - task->enable_scheduler = true; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + if (ctx && !ctx->enable_scheduler) { + ctx->enable_scheduler = true; return true; } return false; } static inline bool disable_scheduler() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - if (task && task->enable_scheduler) { - task->enable_scheduler = false; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + if (ctx && ctx->enable_scheduler) { + ctx->enable_scheduler = false; return true; } return false; @@ -208,34 +247,64 @@ class PHPCoroutine { return activated; } + static inline long get_execute_time(long cid = 0) { + return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1; + } + + static inline void init_main_context() { + main_context.co = nullptr; +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + main_context.fiber_context = EG(main_fiber_context); + main_context.fiber_init_notified = true; +#endif + save_context(&main_context); + } + + static inline void free_main_context() { + main_context = {}; + } + protected: - static bool activated; - static PHPContext main_task; - static Config config; + static SW_THREAD_LOCAL bool activated; + static SW_THREAD_LOCAL PHPContext main_context; + static SW_THREAD_LOCAL Config config; - static bool interrupt_thread_running; - static std::thread interrupt_thread; + static SW_THREAD_LOCAL bool interrupt_thread_running; + static SW_THREAD_LOCAL std::thread interrupt_thread; static void activate(); static void deactivate(void *ptr); - static inline void vm_stack_init(void); - static inline void vm_stack_destroy(void); - static inline void save_vm_stack(PHPContext *task); - static inline void restore_vm_stack(PHPContext *task); - static inline void save_og(PHPContext *task); - static inline void restore_og(PHPContext *task); - static inline void save_task(PHPContext *task); - static inline void restore_task(PHPContext *task); + static void save_vm_stack(PHPContext *ctx); + static void restore_vm_stack(PHPContext *ctx); + static void save_og(PHPContext *ctx); + static void restore_og(PHPContext *ctx); + static void save_context(PHPContext *ctx); + static void restore_context(PHPContext *ctx); + static void destroy_context(PHPContext *ctx); + static bool catch_exception(); + static void bailout(); static void on_yield(void *arg); static void on_resume(void *arg); static void on_close(void *arg); static void main_func(void *arg); - +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + static zend_fiber_status get_fiber_status(PHPContext *ctx); + static void fiber_context_init(PHPContext *ctx); + static void fiber_context_try_init(PHPContext *ctx); + static void fiber_context_destroy(PHPContext *ctx); + static void fiber_context_try_destroy(PHPContext *ctx); + static void fiber_context_switch_notify(PHPContext *from, PHPContext *to); + static void fiber_context_switch_try_notify(PHPContext *from, PHPContext *to); +#endif +#ifdef ZEND_CHECK_STACK_LIMIT + static void* stack_limit(PHPContext *ctx); + static void* stack_base(PHPContext *ctx); +#endif static void interrupt_thread_start(); - static inline void record_last_msec(PHPContext *task) { + static void record_last_msec(PHPContext *ctx) { if (interrupt_thread_running) { - task->last_msec = Timer::get_absolute_msec(); + ctx->last_msec = Timer::get_absolute_msec(); } } }; diff --git a/ext-src/php_swoole_coroutine_system.h b/ext-src/php_swoole_coroutine_system.h index 16b7d7a9e8a..f4a75506e87 100644 --- a/ext-src/php_swoole_coroutine_system.h +++ b/ext-src/php_swoole_coroutine_system.h @@ -19,85 +19,9 @@ #include "php_swoole_cxx.h" // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_exec, 0, 0, 1) - ZEND_ARG_INFO(0, command) - ZEND_ARG_INFO(0, get_error_stream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_sleep, 0, 0, 1) - ZEND_ARG_INFO(0, seconds) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fread, 0, 0, 1) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fgets, 0, 0, 1) - ZEND_ARG_INFO(0, handle) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fwrite, 0, 0, 2) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_gethostbyname, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_dnsLookup, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_getaddrinfo, 0, 0, 1) - ZEND_ARG_INFO(0, hostname) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, socktype) - ZEND_ARG_INFO(0, protocol) - ZEND_ARG_INFO(0, service) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_readFile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_writeFile, 0, 0, 2) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_statvfs, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitPid, 0, 0, 1) - ZEND_ARG_INFO(0, pid) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitSignal, 0, 0, 1) - ZEND_ARG_INFO(0, signo) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitEvent, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_system_arginfo.h" +END_EXTERN_C() // clang-format on SW_EXTERN_C_BEGIN diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index 97ef863dc88..3cd4ee67a54 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,26 +24,50 @@ SW_EXTERN_C_BEGIN #include #include +#if PHP_VERSION_ID >= 80400 +#include "thirdparty/php84/curl/curl_private.h" +#else #include "thirdparty/php/curl/curl_private.h" +#endif SW_EXTERN_C_END -namespace swoole { +#if LIBCURL_VERSION_NUM < 0x073800 +#error "require cURL version 7.56.0 or later" +#endif -using network::Socket; +CURLcode swoole_curl_easy_perform(CURL *cp); +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); +namespace swoole { namespace curl { class Multi; -struct Handle { - CURL *cp; - Socket *socket; - Multi *multi; +struct HandleSocket { + network::Socket *socket; int event_bitmask; int event_fd; int action; }; +struct Handle { + CURL *cp; + Multi *multi; + std::unordered_map sockets; + + Handle(CURL *_cp) { + cp = _cp; + multi = nullptr; + } + + HandleSocket *create_socket(curl_socket_t sockfd); + void destroy_socket(curl_socket_t sockfd); +}; + +Handle *get_handle(CURL *cp); +Handle *create_handle(CURL *ch); +void destroy_handle(CURL *ch); + struct Selector { bool timer_callback = false; std::set active_handles; @@ -62,16 +86,10 @@ class Multi { CURLcode read_info(); - Socket *create_socket(CURL *cp, curl_socket_t sockfd); - - Handle *get_handle(CURL *cp) { - Handle *handle = nullptr; - curl_easy_getinfo(cp, CURLINFO_PRIVATE, &handle); - return handle; - } + HandleSocket *create_socket(Handle *handle, curl_socket_t sockfd); - void set_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd, int action); - void del_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd); + void set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int action); + void del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd); void add_timer(long timeout_ms) { if (timer && swoole_timer_is_available()) { @@ -109,6 +127,7 @@ class Multi { } ~Multi() { + del_timer(); curl_multi_cleanup(multi_handle_); } @@ -124,8 +143,8 @@ class Multi { selector.reset(_selector); } - CURLMcode add_handle(CURL *cp); - CURLMcode remove_handle(CURL *cp); + CURLMcode add_handle(Handle *handle); + CURLMcode remove_handle(Handle *handle); CURLMcode perform() { return curl_multi_perform(multi_handle_, &running_handles_); @@ -143,9 +162,9 @@ class Multi { return Coroutine::get_current_safe(); } - CURLcode exec(php_curl *ch); + CURLcode exec(Handle *handle); long select(php_curlm *mh, double timeout = -1); - void callback(Handle *handle, int event_bitmask); + void callback(Handle *handle, int event_bitmask, int sockfd = -1); static int cb_readable(Reactor *reactor, Event *event); static int cb_writable(Reactor *reactor, Event *event); diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index 3a08191e8b7..54bee9168ea 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -1,6 +1,6 @@ #include "php_swoole_cxx.h" -//----------------------------------Swoole known string------------------------------------ +//----------------------------------known string------------------------------------ static const char *sw_known_strings[] = { #define _SW_ZEND_STR_DSC(id, str) str, @@ -10,38 +10,14 @@ static const char *sw_known_strings[] = { SW_API zend_string **sw_zend_known_strings = nullptr; -//----------------------------------Swoole known string------------------------------------ +SW_API zend_refcounted *sw_refcount_ptr; -#if PHP_VERSION_ID < 80000 -typedef zval zend_source_string_t; -#else -typedef zend_string zend_source_string_t; -#endif - -static zend_op_array *swoole_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename); - -// for compatibly with dis_eval -static zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename); - -static zend_op_array *swoole_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename) { - zend_op_array *opa = old_compile_string(source_string, filename); - opa->type = ZEND_USER_FUNCTION; - return opa; +zend_refcounted *sw_get_refcount_ptr(zval *value) { + return (sw_refcount_ptr = value->value.counted); } +//----------------------------------known string------------------------------------ namespace zend { -bool eval(const std::string &code, std::string const &filename) { - if (!old_compile_string) { - old_compile_string = zend_compile_string; - } - // overwrite - zend_compile_string = swoole_compile_string; - int ret = (zend_eval_stringl((char *) code.c_str(), code.length(), nullptr, (char *) filename.c_str()) == SUCCESS); - // recover - zend_compile_string = old_compile_string; - return ret; -} - void known_strings_init(void) { zend_string *str; sw_zend_known_strings = nullptr; @@ -69,7 +45,7 @@ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *ret /* the coroutine has no return value */ ZVAL_NULL(retval); } - success = swoole::PHPCoroutine::create(fci_cache, argc, argv) >= 0; + success = swoole::PHPCoroutine::create(fci_cache, argc, argv, nullptr) >= 0; } else { success = sw_zend_call_function_ex(nullptr, fci_cache, argc, argv, retval) == SUCCESS; } @@ -80,10 +56,10 @@ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *ret return success; } -ReturnValue call(const std::string &func_name, int argc, zval *argv) { +Variable call(const std::string &func_name, int argc, zval *argv) { zval function_name; ZVAL_STRINGL(&function_name, func_name.c_str(), func_name.length()); - ReturnValue retval; + Variable retval; if (call_user_function(EG(function_table), NULL, &function_name, &retval.value, argc, argv) != SUCCESS) { ZVAL_NULL(&retval.value); } @@ -96,4 +72,31 @@ ReturnValue call(const std::string &func_name, int argc, zval *argv) { } } // namespace function + +Callable::Callable(zval *_zfn) { + ZVAL_UNDEF(&zfn); + if (!zval_is_true(_zfn)) { + php_swoole_fatal_error(E_WARNING, "illegal callback function"); + return; + } + if (!sw_zend_is_callable_ex(_zfn, nullptr, 0, &fn_name, nullptr, &fcc, nullptr)) { + php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", fn_name); + return; + } + zfn = *_zfn; + zval_add_ref(&zfn); +} + +Callable::~Callable() { + if (!ZVAL_IS_UNDEF(&zfn)) { + zval_ptr_dtor(&zfn); + } + if (fn_name) { + efree(fn_name); + } +} + +uint32_t Callable::refcount() { + return zval_refcount_p(&zfn); +} } // namespace zend diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 2e668620d19..c3b19b27ccb 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -28,6 +28,13 @@ #define SW_ZEND_KNOWN_STRINGS(_) \ _(SW_ZEND_STR_TYPE, "type") \ _(SW_ZEND_STR_HOST, "host") \ + _(SW_ZEND_STR_USER_AGENT, "user-agent") \ + _(SW_ZEND_STR_ACCEPT, "accept") \ + _(SW_ZEND_STR_CONTENT_TYPE, "content-type") \ + _(SW_ZEND_STR_CONTENT_LENGTH, "content-length") \ + _(SW_ZEND_STR_AUTHORIZATION, "authorization") \ + _(SW_ZEND_STR_CONNECTION, "connection") \ + _(SW_ZEND_STR_ACCEPT_ENCODING, "accept-encoding") \ _(SW_ZEND_STR_PORT, "port") \ _(SW_ZEND_STR_SETTING, "setting") \ _(SW_ZEND_STR_ID, "id") \ @@ -35,7 +42,6 @@ _(SW_ZEND_STR_SOCK, "sock") \ _(SW_ZEND_STR_PIPE, "pipe") \ _(SW_ZEND_STR_HEADERS, "headers") \ - _(SW_ZEND_STR_SET_COOKIE_HEADERS, "set_cookie_headers") \ _(SW_ZEND_STR_REQUEST_METHOD, "requestMethod") \ _(SW_ZEND_STR_REQUEST_HEADERS, "requestHeaders") \ _(SW_ZEND_STR_REQUEST_BODY, "requestBody") \ @@ -43,8 +49,12 @@ _(SW_ZEND_STR_COOKIES, "cookies") \ _(SW_ZEND_STR_DOWNLOAD_FILE, "downloadFile") \ _(SW_ZEND_STR_DOWNLOAD_OFFSET, "downloadOffset") \ - _(SW_ZEND_STR_TMPFILES, "tmpfiles") \ + _(SW_ZEND_STR_SERVER, "server") \ _(SW_ZEND_STR_HEADER, "header") \ + _(SW_ZEND_STR_GET, "get") \ + _(SW_ZEND_STR_POST, "post") \ + _(SW_ZEND_STR_FILES, "files") \ + _(SW_ZEND_STR_TMPFILES, "tmpfiles") \ _(SW_ZEND_STR_COOKIE, "cookie") \ _(SW_ZEND_STR_METHOD, "method") \ _(SW_ZEND_STR_PATH, "path") \ @@ -54,13 +64,30 @@ _(SW_ZEND_STR_TRAILER, "trailer") \ _(SW_ZEND_STR_MASTER_PID, "master_pid") \ _(SW_ZEND_STR_CALLBACK, "callback") \ - _(SW_ZEND_STR_VALUE, "value") \ - _(SW_ZEND_STR_KEY, "key") \ _(SW_ZEND_STR_OPCODE, "opcode") \ _(SW_ZEND_STR_CODE, "code") \ _(SW_ZEND_STR_REASON, "reason") \ _(SW_ZEND_STR_FLAGS, "flags") \ _(SW_ZEND_STR_FINISH, "finish") \ + _(SW_ZEND_STR_IN_COROUTINE, "in_coroutine") \ + _(SW_ZEND_STR_PRIVATE_DATA, "private_data") \ + _(SW_ZEND_STR_CLASS_NAME_RESOLVER, "Swoole\\NameResolver") \ + _(SW_ZEND_STR_SOCKET, "socket") \ + _(SW_ZEND_STR_ADDR_LOOPBACK_V4, "127.0.0.1") \ + _(SW_ZEND_STR_ADDR_LOOPBACK_V6, "::1") \ + _(SW_ZEND_STR_REQUEST_METHOD2, "request_method") \ + _(SW_ZEND_STR_REQUEST_URI, "request_uri") \ + _(SW_ZEND_STR_PATH_INFO, "path_info") \ + _(SW_ZEND_STR_REQUEST_TIME, "request_time") \ + _(SW_ZEND_STR_REQUEST_TIME_FLOAT, "request_time_float") \ + _(SW_ZEND_STR_SERVER_PROTOCOL, "server_protocol") \ + _(SW_ZEND_STR_SERVER_PORT, "server_port") \ + _(SW_ZEND_STR_REMOTE_PORT, "remote_port") \ + _(SW_ZEND_STR_REMOTE_ADDR, "remote_addr") \ + _(SW_ZEND_STR_MASTER_TIME, "master_time") \ + _(SW_ZEND_STR_QUERY_STRING, "query_string") \ + _(SW_ZEND_STR_HTTP10, "HTTP/1.0") \ + _(SW_ZEND_STR_HTTP11, "HTTP/1.1") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, @@ -79,67 +106,115 @@ extern zend_string **sw_zend_known_strings; #define SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(module) \ module##_ce->create_object = [](zend_class_entry *ce) { return sw_zend_create_object(ce, &module##_handlers); } +/** + * It is safe across coroutines, + * add reference count, prevent the socket pointer being released + */ +#define SW_CLIENT_GET_SOCKET_SAFE(__sock, __zsocket) \ + Socket *__sock = nullptr; \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + __sock = php_swoole_get_socket(__zsocket); \ + tmp_socket.assign(__zsocket); \ + } + +#define SW_CLIENT_PRESERVE_SOCKET(__zsocket) \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + tmp_socket.assign(__zsocket); \ + } SW_API bool php_swoole_is_enable_coroutine(); +SW_API zend_object *php_swoole_create_socket(enum swSocketType type); SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type); +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol); SW_API bool php_swoole_export_socket(zval *zobject, swoole::coroutine::Socket *_socket); SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type); SW_API void php_swoole_init_socket_object(zval *zobject, swoole::coroutine::Socket *socket); SW_API swoole::coroutine::Socket *php_swoole_get_socket(zval *zobject); +SW_API bool php_swoole_socket_is_closed(zval *zobject); #ifdef SW_USE_OPENSSL SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zset); #endif SW_API bool php_swoole_socket_set_protocol(swoole::coroutine::Socket *sock, zval *zset); -SW_API bool php_swoole_client_set(swoole::coroutine::Socket *cli, zval *zset); -SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC); +SW_API bool php_swoole_socket_set(swoole::coroutine::Socket *cli, zval *zset); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, swoole::coroutine::Socket *socket); +#define php_swoole_client_set php_swoole_socket_set +SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, + int domain, + int type, + int protocol STREAMS_DC); +SW_API php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC); SW_API php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops(); SW_API void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data); +SW_API zif_handler php_swoole_get_original_handler(const char *name, size_t len); +SW_API bool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS); // timer SW_API bool php_swoole_timer_clear(swoole::TimerNode *tnode); SW_API bool php_swoole_timer_clear_all(); static inline bool php_swoole_is_fatal_error() { - if (PG(last_error_message)) { - switch (PG(last_error_type)) { - case E_ERROR: - case E_CORE_ERROR: - case E_USER_ERROR: - case E_COMPILE_ERROR: - return true; - default: - break; - } - } - return false; + return PG(last_error_message) && (PG(last_error_type) & E_FATAL_ERRORS); } -ssize_t php_swoole_length_func(swoole::Protocol *protocol, swoole::network::Socket *_socket, const char *data, uint32_t length); +ssize_t php_swoole_length_func(const swoole::Protocol *, swoole::network::Socket *, swoole::PacketLength *); +SW_API zend_long php_swoole_parse_to_size(zval *zv); +SW_API zend_string *php_swoole_serialize(zval *zdata); +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv); #ifdef SW_HAVE_ZLIB -#define php_swoole_websocket_frame_pack php_swoole_websocket_frame_pack_ex +#define php_swoole_websocket_frame_pack php_swoole_websocket_frame_pack_ex #define php_swoole_websocket_frame_object_pack php_swoole_websocket_frame_object_pack_ex #else -#define php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags, mask, allow_compress) \ - php_swoole_websocket_frame_pack_ex(buffer, zdata, opcode, flags, mask, 0) -#define php_swoole_websocket_frame_object_pack(buffer, zdata, mask, allow_compress) \ - php_swoole_websocket_frame_object_pack_ex(buffer, zdata, mask, 0) +#define php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags, mask, allow_compress) \ + php_swoole_websocket_frame_pack_ex(buffer, zdata, opcode, flags, mask, 0) +#define php_swoole_websocket_frame_object_pack(buffer, zdata, mask, allow_compress) \ + php_swoole_websocket_frame_object_pack_ex(buffer, zdata, mask, 0) #endif -int php_swoole_websocket_frame_pack_ex(swoole::String *buffer, zval *zdata, zend_long opcode, uint8_t flags, zend_bool mask, zend_bool allow_compress); -int php_swoole_websocket_frame_object_pack_ex(swoole::String *buffer, zval *zdata, zend_bool mask, zend_bool allow_compress); +int php_swoole_websocket_frame_pack_ex( + swoole::String *buffer, zval *zdata, zend_long opcode, uint8_t flags, zend_bool mask, zend_bool allow_compress); +int php_swoole_websocket_frame_object_pack_ex(swoole::String *buffer, + zval *zdata, + zend_bool mask, + zend_bool allow_compress); void php_swoole_websocket_frame_unpack(swoole::String *data, zval *zframe); void php_swoole_websocket_frame_unpack_ex(swoole::String *data, zval *zframe, uchar allow_uncompress); -swoole::TaskId php_swoole_task_pack(swoole::EventData *task, zval *data); -zval* php_swoole_task_unpack(swoole::EventData *task_result); - #ifdef SW_HAVE_ZLIB int php_swoole_zlib_decompress(z_stream *stream, swoole::String *buffer, char *body, int length); #endif +swoole::NameResolver::Context *php_swoole_name_resolver_get_context(zval *zobject); +std::string php_swoole_name_resolver_lookup(const std::string &name, + swoole::NameResolver::Context *ctx, + void *_resolver); +bool php_swoole_name_resolver_add(zval *zresolver); + const swoole::Allocator *sw_php_allocator(); const swoole::Allocator *sw_zend_string_allocator(); +#ifdef __APPLE__ +#define SOL_TCP IPPROTO_TCP +#define TCP_INFO TCP_CONNECTION_INFO +using tcp_info = tcp_connection_info; +#endif + +#ifdef TCP_INFO +std::unordered_map sw_socket_parse_tcp_info(tcp_info *info); +#endif + +static inline bool php_swoole_async(bool blocking, const std::function &fn) { + if (!blocking && swoole_coroutine_is_in()) { + return swoole::coroutine::async(fn); + } else { + fn(); + return true; + } +} + namespace zend { //-----------------------------------namespace begin-------------------------------------------- class String { @@ -178,32 +253,32 @@ class String { } void operator=(zval *v) { - if (str) { - zend_string_release(str); - } + release(); str = zval_get_string(v); } String &operator=(String &&o) { + release(); str = o.str; o.str = nullptr; return *this; } String &operator=(const String &o) { + release(); str = zend_string_copy(o.str); return *this; } - inline char *val() { + char *val() { return ZSTR_VAL(str); } - inline size_t len() { + size_t len() { return ZSTR_LEN(str); } - inline zend_string *get() { + zend_string *get() { return str; } @@ -211,19 +286,19 @@ class String { ZSTR_LEN(str) = swoole::rtrim(val(), len()); } - inline const std::string to_std_string() { + const std::string to_std_string() { return std::string(val(), len()); } - inline char *dup() { + char *dup() { return sw_likely(len() > 0) ? sw_strndup(val(), len()) : nullptr; } - inline char *edup() { + char *edup() { return sw_likely(len() > 0) ? estrndup(val(), len()) : nullptr; } - inline void release() { + void release() { if (str) { zend_string_release(str); str = nullptr; @@ -252,7 +327,7 @@ class KeyValue { Z_TRY_ADDREF(zvalue); } - inline void add_to(zval *zarray) { + void add_to(zval *zarray) { HashTable *ht = Z_ARRVAL_P(zarray); zval *dest_elem = !key ? zend_hash_index_update(ht, index, &zvalue) : zend_hash_update(ht, key, &zvalue); Z_TRY_ADDREF_P(dest_elem); @@ -275,6 +350,7 @@ class ArrayIterator { _index = _ptr->h; pe = p; } + ArrayIterator(Bucket *p, Bucket *_pe) { _ptr = p; _key = _ptr->key; @@ -283,22 +359,28 @@ class ArrayIterator { pe = _pe; skipUndefBucket(); } + void operator++(int i) { ++_ptr; skipUndefBucket(); } + bool operator!=(ArrayIterator b) { return b.ptr() != _ptr; } + std::string key() { return std::string(_key->val, _key->len); } + zend_ulong index() { return _index; } + zval *value() { return _val; } + Bucket *ptr() { return _ptr; } @@ -341,19 +423,19 @@ class Array { arr = _arr; } - inline size_t count() { + size_t count() { return zend_hash_num_elements(Z_ARRVAL_P(arr)); } - inline bool set(zend_ulong index, zval *value) { + bool set(zend_ulong index, zval *value) { return add_index_zval(arr, index, value) == SUCCESS; } - inline bool append(zval *value) { + bool append(zval *value) { return add_next_index_zval(arr, value) == SUCCESS; } - inline bool set(zend_ulong index, zend_resource *res) { + bool set(zend_ulong index, zend_resource *res) { zval tmp; ZVAL_RES(&tmp, res); return set(index, &tmp); @@ -390,40 +472,240 @@ class Process { } }; -namespace function { -/* must use this API to call event callbacks to ensure that exceptions are handled correctly */ -bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); - -class ReturnValue { +class Variable { public: zval value; - ReturnValue() { + + Variable() { value = {}; } - ~ReturnValue() { - zval_dtor(&value); + + Variable(zval *zvalue) { + assign(zvalue); + } + + Variable(const char *str, size_t l_str) { + ZVAL_STRINGL(&value, str, l_str); + } + + Variable(const char *str) { + ZVAL_STRING(&value, str); + } + + Variable(const Variable &&src) { + value = src.value; + add_ref(); + } + + Variable(Variable &&src) { + value = src.value; + src.reset(); + } + + void operator=(zval *zvalue) { + assign(zvalue); + } + + void operator=(const Variable &src) { + value = src.value; + add_ref(); + } + + void assign(zval *zvalue) { + value = *zvalue; + add_ref(); + } + + zval *ptr() { + return &value; + } + + void reset() { + ZVAL_UNDEF(&value); + } + + void add_ref() { + Z_TRY_ADDREF_P(&value); + } + + void del_ref() { + Z_TRY_DELREF_P(&value); + } + + ~Variable() { + zval_ptr_dtor(&value); + } +}; + +class CharPtr { + private: + char *str_; + + public: + CharPtr() { + str_ = nullptr; + } + + CharPtr(char *str) { + str_ = estrndup(str, strlen(str)); + } + + CharPtr(char *str, size_t len) { + str_ = estrndup(str, len); + } + + void operator=(char *str) { + assign(str, strlen(str)); + } + + void release() { + if (str_) { + efree(str_); + str_ = nullptr; + } + } + + void assign(char *str, size_t len) { + release(); + str_ = estrndup(str, len); + } + + void assign_tolower(const char *str, size_t len) { + release(); + str_ = zend_str_tolower_dup(str, len); + } + + ~CharPtr() { + release(); + } + + char *get() { + return str_; } }; -ReturnValue call(const std::string &func_name, int argc, zval *argv); +class Callable { + private: + zval zfn; + zend_fcall_info_cache fcc; + char *fn_name = nullptr; + + Callable() {} + + public: + Callable(zval *_zfn); + ~Callable(); + uint32_t refcount(); + + zend_refcounted *refcount_ptr() { + return sw_get_refcount_ptr(&zfn); + } + + zend_fcall_info_cache *ptr() { + return &fcc; + } + + bool ready() { + return !ZVAL_IS_UNDEF(&zfn); + } + + Callable *dup() { + auto copy = new Callable(); + copy->fcc = fcc; + copy->zfn = zfn; + zval_add_ref(©->zfn); + if (fn_name) { + copy->fn_name = estrdup(fn_name); + } + return copy; + } + + bool call(uint32_t argc, zval *argv, zval *retval) { + return sw_zend_call_function_ex(&zfn, &fcc, argc, argv, retval) == SUCCESS; + } +}; + +#define _CONCURRENCY_HASHMAP_LOCK_(code) \ + if (locked_) { \ + code; \ + } else { \ + lock_.lock(); \ + code; \ + lock_.unlock(); \ + } + +template +class ConcurrencyHashMap { + private: + std::unordered_map map_; + std::mutex lock_; + bool locked_; + ValueT default_value_; + + public: + ConcurrencyHashMap(ValueT _default_value) : map_(), lock_() { + default_value_ = _default_value; + locked_ = false; + } + + void set(const KeyT &key, const ValueT &value) { + _CONCURRENCY_HASHMAP_LOCK_(map_[key] = value); + } + + ValueT get(const KeyT &key) { + ValueT value; + auto fn = [&]() -> ValueT { + auto iter = map_.find(key); + if (iter == map_.end()) { + return default_value_; + } + return iter->second; + }; + _CONCURRENCY_HASHMAP_LOCK_(value = fn()); + return value; + } + + void del(const KeyT &key) { + _CONCURRENCY_HASHMAP_LOCK_(map_.erase(key)); + } + + void clear() { + _CONCURRENCY_HASHMAP_LOCK_(map_.clear()); + } + + void each(const std::function &cb) { + std::unique_lock _lock(lock_); + locked_ = true; + for (auto &iter : map_) { + cb(iter.first, iter.second); + } + locked_ = false; + } +}; + +namespace function { +/* must use this API to call event callbacks to ensure that exceptions are handled correctly */ +bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); +Variable call(const std::string &func_name, int argc, zval *argv); + +static inline bool call(Callable *cb, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) { + return call(cb->ptr(), argc, argv, retval, enable_coroutine); +} } // namespace function struct Function { zend_fcall_info fci; zend_fcall_info_cache fci_cache; - inline bool call(zval *retval, const bool enable_coroutine) { + bool call(zval *retval, const bool enable_coroutine) { return function::call(&fci_cache, fci.param_count, fci.params, retval, enable_coroutine); } }; -bool eval(const std::string &code, const std::string &filename = ""); void known_strings_init(void); void known_strings_dtor(void); void unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options); -#ifdef SW_USE_JSON void json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long zend_long); -#endif static inline zend_string *fetch_zend_string_by_val(void *val) { return (zend_string *) ((char *) val - XtOffsetOf(zend_string, val)); @@ -436,13 +718,109 @@ static inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t len ZVAL_STR(zdata, zstr); } -#if PHP_VERSION_ID < 80000 -#define ZEND_STR_CONST -#else -#define ZEND_STR_CONST const -#endif +static inline void array_set(zval *arg, const char *key, size_t l_key, zval *zvalue) { + Z_TRY_ADDREF_P(zvalue); + add_assoc_zval_ex(arg, key, l_key, zvalue); +} + +static inline void array_set(zval *arg, const char *key, size_t l_key, const char *value, size_t l_value) { + zval ztmp; + ZVAL_STRINGL(&ztmp, value, l_value); + add_assoc_zval_ex(arg, key, l_key, &ztmp); +} + +static inline void array_add(zval *arg, zval *zvalue) { + Z_TRY_ADDREF_P(zvalue); + add_next_index_zval(arg, zvalue); +} + +/** + * return reference + */ +static inline zval *array_get(zval *arg, const char *key, size_t l_key) { + return zend_hash_str_find(Z_ARRVAL_P(arg), key, l_key); +} + +static inline void array_unset(zval *arg, const char *key, size_t l_key) { + zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); +} + +static inline zend_long object_get_long(zval *obj, zend_string *key) { + static zval rv; + zval *property = zend_read_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline zend_long object_get_long(zval *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline zend_long object_get_long(zend_object *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(obj->ce, obj, key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, zval *zvalue) { + zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, zvalue); +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, const char *value) { + zend_update_property_string(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, zend_long value) { + zend_update_property_long(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); +} + +static inline zval *object_get(zval *obj, const char *name, size_t l_name) { + static zval rv; + return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, 1, &rv); +} + +/** + * print exception, The virtual machine will not be terminated. + */ +static inline void print_error(zend_object *exception, int severity) { + zend_exception_error(exception, severity); +} //-----------------------------------namespace end-------------------------------------------- } // namespace zend +/* use void* to match some C callback function pointers */ +static inline void sw_callable_free(void *ptr) { + delete (zend::Callable *) ptr; +} + +static inline zend::Callable *sw_callable_create(zval *zfn) { + auto fn = new zend::Callable(zfn); + if (fn->ready()) { + return fn; + } else { + delete fn; + return nullptr; + } +} +static inline zend::Callable *sw_callable_create_ex(zval *zfn, const char *fname, bool allow_null = true) { + if (zfn == nullptr || ZVAL_IS_NULL(zfn)) { + if (!allow_null) { + zend_throw_exception_ex( + swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "%s must be of type callable, null given", fname); + } + return nullptr; + } + auto cb = sw_callable_create(zfn); + if (!cb) { + zend_throw_exception_ex(swoole_exception_ce, + SW_ERROR_INVALID_PARAMS, + "%s must be of type callable, %s given", + fname, + zend_zval_type_name(zfn)); + return nullptr; + } + return cb; +} diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index f9a13a97f36..bf378da0323 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -12,20 +12,19 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once #include "swoole_http.h" -#ifdef SW_USE_HTTP2 #include "swoole_http2.h" -#endif #include "thirdparty/swoole_http_parser.h" #include "thirdparty/multipart_parser.h" #include +#include #ifdef SW_HAVE_ZLIB #include @@ -35,11 +34,18 @@ #define SW_ZLIB_ENCODING_ANY 0x2f #endif -#ifdef SW_USE_HTTP2 -#include "thirdparty/nghttp2/nghttp2.h" +#ifdef SW_HAVE_BROTLI +#include +#include +#endif + +#ifdef SW_HAVE_ZSTD +#include #endif -enum http_header_flag { +#include + +enum swHttpHeaderFlag { HTTP_HEADER_SERVER = 1u << 1, HTTP_HEADER_CONNECTION = 1u << 2, HTTP_HEADER_CONTENT_LENGTH = 1u << 3, @@ -47,24 +53,24 @@ enum http_header_flag { HTTP_HEADER_CONTENT_TYPE = 1u << 5, HTTP_HEADER_TRANSFER_ENCODING = 1u << 6, HTTP_HEADER_ACCEPT_ENCODING = 1u << 7, + HTTP_HEADER_CONTENT_ENCODING = 1u << 8, }; -enum http_compress_method { +enum swHttpCompressMethod { HTTP_COMPRESS_NONE, HTTP_COMPRESS_GZIP, HTTP_COMPRESS_DEFLATE, HTTP_COMPRESS_BR, + HTTP_COMPRESS_ZSTD, }; namespace swoole { class Server; -#ifdef SW_USE_HTTP2 class Coroutine; namespace http2 { class Stream; class Session; } // namespace http2 -#endif namespace http { @@ -77,11 +83,10 @@ struct Request { uint8_t post_form_urlencoded; zval zdata; + const char *body_at; size_t body_length; String *chunked_body; -#ifdef SW_USE_HTTP2 String *h2_data_buffer; -#endif // Notice: Do not change the order zval *zobject; @@ -127,6 +132,7 @@ struct Context { #ifdef SW_HAVE_COMPRESSION uchar enable_compression : 1; uchar accept_compression : 1; + uchar content_compressed : 1; #endif uchar send_chunked : 1; uchar recv_chunked : 1; @@ -142,16 +148,17 @@ struct Context { uchar parse_body : 1; uchar parse_files : 1; uchar co_socket : 1; - -#ifdef SW_USE_HTTP2 uchar http2 : 1; + http2::Stream *stream; -#endif + String *write_buffer; #ifdef SW_HAVE_COMPRESSION int8_t compression_level; int8_t compression_method; uint32_t compression_min_length; + std::shared_ptr> compression_types; + std::shared_ptr zlib_buffer; #endif Request request; @@ -161,49 +168,104 @@ struct Context { multipart_parser *mt_parser; uint16_t input_var_num; - char *current_header_name; + const char *current_header_name; size_t current_header_name_len; char *current_input_name; size_t current_input_name_len; char *current_form_data_name; size_t current_form_data_name_len; zval *current_multipart_header; + const char *tmp_content_type; + size_t tmp_content_type_len; + String *form_data_buffer; std::string upload_tmp_dir; void *private_data; + void *private_data_2; bool (*send)(Context *ctx, const char *data, size_t length); bool (*sendfile)(Context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); bool (*close)(Context *ctx); + bool (*onBeforeRequest)(Context *ctx); + void (*onAfterResponse)(Context *ctx); void init(Server *server); void init(coroutine::Socket *socket); void bind(Server *server); void bind(coroutine::Socket *socket); void copy(Context *ctx); - bool parse_form_data(const char *boundary_str, int boundary_len); + bool init_multipart_parser(const char *boundary_str, int boundary_len); + bool get_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); size_t parse(const char *data, size_t length); + bool parse_multipart_data(const char *at, size_t length); bool set_header(const char *, size_t, zval *, bool); bool set_header(const char *, size_t, const char *, size_t, bool); void end(zval *zdata, zval *return_value); + void write(zval *zdata, zval *return_value); + bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length); void send_trailer(zval *return_value); String *get_write_buffer(); + void build_header(String *http_buffer, const char *body, size_t length); + ssize_t build_trailer(String *http_buffer); + + size_t get_content_length() { + return parser.content_length; + } #ifdef SW_HAVE_COMPRESSION void set_compression_method(const char *accept_encoding, size_t length); const char *get_content_encoding(); + bool compress(const char *data, size_t length); #endif -#ifdef SW_USE_HTTP2 void http2_end(zval *zdata, zval *return_value); -#endif + void http2_write(zval *zdata, zval *return_value); + bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length); + bool is_available(); void free(); }; +class Cookie { + private: + bool encode_; + smart_str buffer_ = {0}; + + protected: + zend_string *name = nullptr; + zend_string *value = nullptr; + zend_string *path = nullptr; + zend_string *domain = nullptr; + zend_string *sameSite = nullptr; + zend_string *priority = nullptr; + zend_long expires = 0; + zend_bool secure = false; + zend_bool httpOnly = false; + zend_bool partitioned = false; + + public: + Cookie(bool _encode = true) { + encode_ = _encode; + } + Cookie *withName(zend_string *); + Cookie *withExpires(zend_long); + Cookie *withSecure(zend_bool); + Cookie *withHttpOnly(zend_bool); + Cookie *withPartitioned(zend_bool); + Cookie *withValue(zend_string *); + Cookie *withPath(zend_string *); + Cookie *withDomain(zend_string *); + Cookie *withSameSite(zend_string *); + Cookie *withPriority(zend_string *); + void reset(); + void toArray(zval *return_value); + zend_string *toString(); + ~Cookie(); +}; + } // namespace http -#ifdef SW_USE_HTTP2 namespace http2 { class Stream { public: @@ -218,8 +280,9 @@ class Stream { Stream(Session *client, uint32_t _id); ~Stream(); - bool send_header(size_t body_length, bool end_stream); - bool send_body(String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); + bool send_header(const String *body, bool end_stream); + bool send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); + bool send_end_stream_data_frame(); bool send_trailer(); void reset(uint32_t error_code); @@ -236,6 +299,10 @@ class Session { http2::Settings local_settings = {}; http2::Settings remote_settings = {}; + // flow control + uint32_t remote_window_size; + uint32_t local_window_size; + uint32_t last_stream_id; bool shutting_down; bool is_coro; @@ -249,23 +316,35 @@ class Session { ~Session(); }; } // namespace http2 -#endif } // namespace swoole extern zend_class_entry *swoole_http_server_ce; extern zend_class_entry *swoole_http_request_ce; extern zend_class_entry *swoole_http_response_ce; - -extern swoole::String *swoole_http_buffer; -extern swoole::String *swoole_http_form_data_buffer; -#ifdef SW_HAVE_COMPRESSION -extern swoole::String *swoole_zlib_buffer; -#endif +extern zend_class_entry *swoole_http_cookie_ce; swoole::http::Context *swoole_http_context_new(swoole::SessionId fd); swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject); swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); +swoole::http::Cookie *php_swoole_http_get_cooke_safety(zval *zobject); + +/** + * These class properties cannot be modified by the user before assignment, such as Swoole\\Http\\Request. + * So we can use this function to init property. + */ +static sw_inline zval *swoole_http_init_and_read_property( + zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, zend_string *name, int size = HT_MIN_SIZE) { + if (UNEXPECTED(!*zproperty_store_pp)) { + zval *zv = zend_hash_find(&ce->properties_info, name); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset); + array_init_size(property, size); + *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); + **zproperty_store_pp = *property; + } + return *zproperty_store_pp; +} static sw_inline zval *swoole_http_init_and_read_property( zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, const char *name, size_t name_len) { @@ -296,17 +375,14 @@ static inline bool swoole_http_has_crlf(const char *value, size_t length) { return false; } -void swoole_http_parse_cookie(zval *array, const char *at, size_t length, bool url_decode = true); +void swoole_http_parse_cookie(zval *array, const char *at, size_t length); +bool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value); swoole::http::Context *php_swoole_http_request_get_context(zval *zobject); void php_swoole_http_request_set_context(zval *zobject, swoole::http::Context *context); swoole::http::Context *php_swoole_http_response_get_context(zval *zobject); void php_swoole_http_response_set_context(zval *zobject, swoole::http::Context *context); -#ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(const char *data, size_t length, int method, int level); -#endif - #ifdef SW_HAVE_ZLIB voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size); void php_zlib_free(voidpf opaque, voidpf address); @@ -317,8 +393,6 @@ void *php_brotli_alloc(void *opaque, size_t size); void php_brotli_free(void *opaque, void *address); #endif -#ifdef SW_USE_HTTP2 - static sw_inline nghttp2_mem *php_nghttp2_mem() { static nghttp2_mem mem = {nullptr, [](size_t size, void *mem_user_data) { return emalloc(size); }, @@ -404,4 +478,3 @@ class HeaderSet { //-----------------------------------namespace end-------------------------------------------- } // namespace http2 } // namespace swoole -#endif diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 321fd5752a7..22855ccfcbc 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,23 +24,64 @@ #include "swoole_http.h" #include "swoole_websocket.h" #include "swoole_mime_type.h" - -#ifdef SW_USE_HTTP2 #include "swoole_http2.h" -#endif + +bool swoole_http_server_onBeforeRequest(swoole::http::Context *ctx); +void swoole_http_server_onAfterResponse(swoole::http::Context *ctx); int swoole_websocket_onMessage(swoole::Server *serv, swoole::RecvData *req); int swoole_websocket_onHandshake(swoole::Server *serv, swoole::ListenPort *port, swoole::http::Context *ctx); +void swoole_websocket_onBeforeHandshakeResponse(swoole::http::Context *ctx); void swoole_websocket_onOpen(swoole::http::Context *ctx); void swoole_websocket_onRequest(swoole::http::Context *ctx); bool swoole_websocket_handshake(swoole::http::Context *ctx); -#ifdef SW_USE_HTTP2 - -int swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req); int swoole_http2_server_parse(swoole::http2::Session *client, const char *buf); +int swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req); void swoole_http2_server_session_free(swoole::Connection *conn); int swoole_http2_server_ping(swoole::http::Context *ctx); -int swoole_http2_server_goaway(swoole::http::Context *ctx, zend_long error_code, const char *debug_data, size_t debug_data_len); +int swoole_http2_server_goaway(swoole::http::Context *ctx, + zend_long error_code, + const char *debug_data, + size_t debug_data_len); + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value) { + zval tmp; + ZVAL_STRING(&tmp, value); + zend_hash_add_new(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value, size_t length) { + zval tmp; + ZVAL_STRINGL(&tmp, value, length); + zend_hash_add_new(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_long value) { + zval tmp; + ZVAL_LONG(&tmp, value); + zend_hash_add_new(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) { + zval tmp; + ZVAL_DOUBLE(&tmp, value); + zend_hash_add_new(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_string *value) { + zval tmp; + ZVAL_STR(&tmp, value); + zend_hash_add_new(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zval *value) { + zend_hash_add_new(ht, key, value); +} -#endif +static inline void http_server_set_object_fd_property(zend_object *object, zend_class_entry *ce, long fd) { + zval *zv = zend_hash_find(&ce->properties_info, SW_ZSTR_KNOWN(SW_ZEND_STR_FD)); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(object, property_info->offset); + ZVAL_LONG(property, fd); +} diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index e06ebfc14e1..78cc7d011d0 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,8 +1,70 @@ /** + * ----------------------------------------------------------------------- * Generated by build-library.php, Please DO NOT modify! + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ */ -/* $Id: 47fac979e2420ba0a43cadb1faacce5d75fe0462 */ +/* $Id: 55873a764b83b7993498004cb1d9d151ae446141 */ + +#ifndef SWOOLE_LIBRARY_H +#define SWOOLE_LIBRARY_H + +#if PHP_VERSION_ID < 80000 +typedef zval zend_source_string_t; +#else +typedef zend_string zend_source_string_t; +#endif + +#if PHP_VERSION_ID < 80200 +#define ZEND_COMPILE_POSITION_DC +#define ZEND_COMPILE_POSITION_RELAY_C +#else +#define ZEND_COMPILE_POSITION_DC , zend_compile_position position +#define ZEND_COMPILE_POSITION_RELAY_C , position +#endif + +#if PHP_VERSION_ID < 80000 +#define ZEND_STR_CONST +#else +#define ZEND_STR_CONST const +#endif + + +static zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC); + +static inline zend_op_array *_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC) { + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + return NULL; + } + zend_op_array *opa = old_compile_string(source_string, filename ZEND_COMPILE_POSITION_RELAY_C); + opa->type = ZEND_USER_FUNCTION; + return opa; +} + +static inline zend_bool _eval(const char *code, const char *filename) { + if (!old_compile_string) { + old_compile_string = zend_compile_string; + } + // overwrite + zend_compile_string = _compile_string; + int ret = (zend_eval_stringl((char *) code, strlen(code), NULL, (char *) filename) == SUCCESS); + // recover + zend_compile_string = old_compile_string; + return ret; +} + +#endif static const char* swoole_library_source_constants = "\n" @@ -35,9 +97,11 @@ static const char* swoole_library_source_std_exec = "\n" "declare(strict_types=1);\n" "\n" + "use Swoole\\Coroutine\\System;\n" + "\n" "function swoole_exec(string $command, &$output = null, &$returnVar = null)\n" "{\n" - " $result = Swoole\\Coroutine::exec($command);\n" + " $result = System::exec($command);\n" " if ($result) {\n" " $outputList = explode(PHP_EOL, $result['output']);\n" " foreach ($outputList as &$value) {\n" @@ -60,7 +124,7 @@ static const char* swoole_library_source_std_exec = "\n" "function swoole_shell_exec(string $cmd)\n" "{\n" - " $result = Swoole\\Coroutine::exec($cmd);\n" + " $result = System::exec($cmd);\n" " if ($result && $result['output'] !== '') {\n" " return $result['output'];\n" " }\n" @@ -128,6 +192,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const EVENT_HANDSHAKE = 'handshake';\n" "\n" + " public const EVENT_BEFORE_HANDSHAKE_RESPONSE = 'beforeHandshakeResponse';\n" + "\n" " public const EVENT_OPEN = 'open';\n" "\n" " public const EVENT_MESSAGE = 'message';\n" @@ -173,7 +239,17 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\n" "\n" - " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" + " public const OPTION_HTTP2_HEADER_TABLE_SIZE = 'http2_header_table_size';\n" + "\n" + " public const OPTION_HTTP2_ENABLE_PUSH = 'http2_enable_push';\n" + "\n" + " public const OPTION_HTTP2_MAX_CONCURRENT_STREAMS = 'http2_max_concurrent_streams';\n" + "\n" + " public const OPTION_HTTP2_INIT_WINDOW_SIZE = 'http2_init_window_size';\n" + "\n" + " public const OPTION_HTTP2_MAX_FRAME_SIZE = 'http2_max_frame_size';\n" + "\n" + " public const OPTION_HTTP2_MAX_HEADER_LIST_SIZE = 'http2_max_header_list_size';\n" "\n" " public const OPTION_AIO_CORE_WORKER_NUM = 'aio_core_worker_num';\n" "\n" @@ -183,6 +259,21 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\n" "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" + " public const OPTION_IOURING_ENTRIES = 'iouring_entries';\n" + "\n" + " /**\n" + " * @since 6.0.0-rc1\n" + " */\n" + " public const OPTION_IOURING_WORKERS = 'iouring_workers';\n" + "\n" + " /**\n" + " * @since 6.0.0-rc1\n" + " */\n" + " public const OPTION_IOURING_FLAG = 'iouring_flag';\n" + "\n" " public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\n" "\n" " public const OPTION_WAIT_SIGNAL = 'wait_signal';\n" @@ -225,6 +316,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SSL_VERIFY_DEPTH = 'ssl_verify_depth';\n" "\n" + " public const OPTION_SSL_CIPHERS = 'ssl_ciphers';\n" + "\n" " public const OPTION_OPEN_EOF_CHECK = 'open_eof_check';\n" "\n" " public const OPTION_OPEN_EOF_SPLIT = 'open_eof_split';\n" @@ -273,16 +366,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_HTTP_PROXY_PASSWORD = 'http_proxy_password';\n" "\n" - " public const OPTION_TIMEOUT = 'timeout';\n" - "\n" - " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" - "\n" - " public const OPTION_READ_TIMEOUT = 'read_timeout';\n" - "\n" - " public const OPTION_WRITE_TIMEOUT = 'write_timeout';\n" - "\n" - " public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\n" - "\n" " public const OPTION_MAX_CORO_NUM = 'max_coro_num';\n" "\n" " public const OPTION_MAX_COROUTINE = 'max_coroutine';\n" @@ -297,11 +380,17 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_STACK_SIZE = 'stack_size';\n" "\n" + " public const OPTION_NAME_RESOLVER = 'name_resolver';\n" + "\n" " public const OPTION_DNS_CACHE_EXPIRE = 'dns_cache_expire';\n" "\n" " public const OPTION_DNS_CACHE_CAPACITY = 'dns_cache_capacity';\n" "\n" - " public const OPTION_RECONNECT = 'reconnect';\n" + " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" + "\n" + " public const OPTION_TIMEOUT = 'timeout';\n" + "\n" + " public const OPTION_MAX_RETRIES = 'max_retries';\n" "\n" " public const OPTION_DEFER = 'defer';\n" "\n" @@ -317,6 +406,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_WEBSOCKET_COMPRESSION = 'websocket_compression';\n" "\n" + " public const OPTION_WRITE_FUNC = 'write_func';\n" + "\n" " public const OPTION_HTTP_PARSE_COOKIE = 'http_parse_cookie';\n" "\n" " public const OPTION_HTTP_PARSE_POST = 'http_parse_post';\n" @@ -325,36 +416,30 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_HTTP_COMPRESSION_LEVEL = 'http_compression_level';\n" "\n" - " public const OPTION_HTTP_GZIP_LEVEL = 'http_gzip_level';\n" - "\n" - " public const OPTION_COMPRESSION_MIN_LENGTH = 'compression_min_length';\n" - "\n" - " public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\n" - "\n" - " public const OPTION_HOST = 'host';\n" - "\n" - " public const OPTION_PORT = 'port';\n" + " public const OPTION_COMPRESSION_LEVEL = 'compression_level';\n" "\n" - " public const OPTION_SSL = 'ssl';\n" + " public const OPTION_HTTP_GZIP_LEVEL = 'http_gzip_level';\n" "\n" - " public const OPTION_USER = 'user';\n" + " public const OPTION_HTTP_COMPRESSION_MIN_LENGTH = 'http_compression_min_length';\n" "\n" - " public const OPTION_PASSWORD = 'password';\n" + " public const OPTION_COMPRESSION_MIN_LENGTH = 'compression_min_length';\n" "\n" - " public const OPTION_DATABASE = 'database';\n" + " public const OPTION_HTTP_COMPRESSION_TYPES = 'http_compression_types';\n" "\n" - " public const OPTION_CHARSET = 'charset';\n" + " public const OPTION_COMPRESSION_TYPES = 'compression_types';\n" "\n" - " public const OPTION_STRICT_TYPE = 'strict_type';\n" + " public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\n" "\n" - " public const OPTION_FETCH_MODE = 'fetch_mode';\n" + " public const OPTION_ENABLE_MESSAGE_BUS = 'enable_message_bus';\n" "\n" - " public const OPTION_SERIALIZE = 'serialize';\n" + " public const OPTION_MAX_PACKAGE_SIZE = 'max_package_size';\n" "\n" - " public const OPTION_COMPATIBILITY_MODE = 'compatibility_mode';\n" + " public const OPTION_SSL = 'ssl';\n" "\n" " public const OPTION_CHROOT = 'chroot';\n" "\n" + " public const OPTION_USER = 'user';\n" + "\n" " public const OPTION_GROUP = 'group';\n" "\n" " public const OPTION_DAEMONIZE = 'daemonize';\n" @@ -371,6 +456,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" "\n" + " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" + "\n" + " public const OPTION_WORKER_MAX_CONCURRENCY = 'worker_max_concurrency';\n" + "\n" " public const OPTION_SEND_TIMEOUT = 'send_timeout';\n" "\n" " public const OPTION_DISPATCH_MODE = 'dispatch_mode';\n" @@ -425,6 +514,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_CPU_AFFINITY_IGNORE = 'cpu_affinity_ignore';\n" "\n" + " public const OPTION_UPLOAD_MAX_FILESIZE = 'upload_max_filesize';\n" + "\n" " public const OPTION_ENABLE_STATIC_HANDLER = 'enable_static_handler';\n" "\n" " public const OPTION_DOCUMENT_ROOT = 'document_root';\n" @@ -445,6 +536,16 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\n" "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" + " public const OPTION_BOOTSTRAP = 'bootstrap';\n" + "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" + " public const OPTION_INIT_ARGUMENTS = 'init_arguments';\n" + "\n" " public const OPTION_BACKLOG = 'backlog';\n" "\n" " public const OPTION_KERNEL_SOCKET_RECV_BUFFER_SIZE = 'kernel_socket_recv_buffer_size';\n" @@ -489,8 +590,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SSL_PREFER_SERVER_CIPHERS = 'ssl_prefer_server_ciphers';\n" "\n" - " public const OPTION_SSL_CIPHERS = 'ssl_ciphers';\n" - "\n" " public const OPTION_SSL_ECDH_CURVE = 'ssl_ecdh_curve';\n" "\n" " public const OPTION_SSL_DHPARAM = 'ssl_dhparam';\n" @@ -501,6 +600,14 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_OPEN_FASTCGI_PROTOCOL = 'open_fastcgi_protocol';\n" "\n" + " public const OPTION_READ_TIMEOUT = 'read_timeout';\n" + "\n" + " public const OPTION_WRITE_TIMEOUT = 'write_timeout';\n" + "\n" + " public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\n" + "\n" + " public const OPTION_SSL_GREASE = 'ssl_grease';\n" + "\n" " public const OPTION_EXIT_CONDITION = 'exit_condition';\n" "\n" " public const OPTION_DEADLOCK_CHECK_DISABLE_TRACE = 'deadlock_check_disable_trace';\n" @@ -509,7 +616,15 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_DEADLOCK_CHECK_DEPTH = 'deadlock_check_depth';\n" "\n" + " public const OPTION_STATS_FILE = 'stats_file';\n" + "\n" + " public const OPTION_STATS_TIMER_INTERVAL = 'stats_timer_interval';\n" + "\n" + " public const OPTION_ADMIN_SERVER = 'admin_server';\n" + "\n" " /* }}} OPTION */\n" + "\n" + " public const OPTION_HTTP_CLIENT_DRIVER = 'http_client_driver';\n" "}\n"; static const char* swoole_library_source_core_string_object = @@ -526,19 +641,13 @@ static const char* swoole_library_source_core_string_object = "\n" "namespace Swoole;\n" "\n" - "class StringObject\n" + "class StringObject implements \\Stringable\n" "{\n" " /**\n" - " * @var string\n" - " */\n" - " protected $string;\n" - "\n" - " /**\n" " * StringObject constructor.\n" " */\n" - " public function __construct(string $string = '')\n" + " public function __construct(protected string $string = '')\n" " {\n" - " $this->string = $string;\n" " }\n" "\n" " public function __toString(): string\n" @@ -546,41 +655,39 @@ static const char* swoole_library_source_core_string_object = " return $this->string;\n" " }\n" "\n" + " public static function from(string $string = ''): static\n" + " {\n" + " return new static($string); // @phpstan-ignore new.static\n" + " }\n" + "\n" " public function length(): int\n" " {\n" " return strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0)\n" + " public function indexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0)\n" + " public function lastIndexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return strrpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0)\n" + " public function pos(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0)\n" + " public function rpos(string $needle, int $offset = 0): false|int\n" + " {\n" + " return strrpos($this->string, $needle, $offset);\n" + " }\n" + "\n" + " public function reverse(): static\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return new static(strrev($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -591,32 +698,22 @@ static const char* swoole_library_source_core_string_object = " return stripos($this->string, $needle);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function lower(): self\n" + " public function lower(): static\n" " {\n" - " return new static(strtolower($this->string));\n" + " return new static(strtolower($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function upper(): self\n" + " public function upper(): static\n" " {\n" - " return new static(strtoupper($this->string));\n" + " return new static(strtoupper($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $characters\n" - " * @return static\n" - " */\n" - " public function trim($characters = ''): self\n" + " public function trim(string $characters = ''): static\n" " {\n" " if ($characters) {\n" - " return new static(trim($this->string, $characters));\n" + " return new static(trim($this->string, $characters)); // @phpstan-ignore new.static\n" " }\n" - " return new static(trim($this->string));\n" + " return new static(trim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -624,7 +721,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function ltrim(): self\n" " {\n" - " return new static(ltrim($this->string));\n" + " return new static(ltrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -632,7 +729,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function rtrim(): self\n" " {\n" - " return new static(rtrim($this->string));\n" + " return new static(rtrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -640,38 +737,30 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function substr(int $offset, ?int $length = null)\n" " {\n" - " return new static(substr($this->string, ...func_get_args()));\n" + " return new static(substr($this->string, $offset, $length)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function repeat(int $times): self\n" + " public function repeat(int $times): static\n" " {\n" - " return new static(str_repeat($this->string, $times));\n" + " return new static(str_repeat($this->string, $times)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $str\n" - " * @return static\n" - " */\n" - " public function append($str): self\n" + " public function append(mixed $str): static\n" " {\n" - " return new static($this->string .= $str);\n" + " return new static($this->string .= $str); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" - " * @param null|int $count\n" - " * @return static\n" + " * @param int|null $count\n" " */\n" - " public function replace(string $search, string $replace, &$count = null): self\n" + " public function replace(string $search, string $replace, &$count = null): static\n" " {\n" - " return new static(str_replace($search, $replace, $this->string, $count));\n" + " return new static(str_replace($search, $replace, $this->string, $count)); // @phpstan-ignore new.static\n" " }\n" "\n" " public function startsWith(string $needle): bool\n" " {\n" - " return strpos($this->string, $needle) === 0;\n" + " return str_starts_with($this->string, $needle);\n" " }\n" "\n" " public function endsWith(string $needle): bool\n" @@ -692,7 +781,7 @@ static const char* swoole_library_source_core_string_object = "\n" " public function contains(string $subString): bool\n" " {\n" - " return strpos($this->string, $subString) !== false;\n" + " return str_contains($this->string, $subString);\n" " }\n" "\n" " public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\n" @@ -709,16 +798,26 @@ static const char* swoole_library_source_core_string_object = " }\n" "\n" " /**\n" - " * @return static\n" + " * Get a new string object by splitting the string of current object into smaller chunks.\n" + " *\n" + " * @param int $length The chunk length.\n" + " * @param string $separator The line ending sequence.\n" + " * @see https://www.php.net/chunk_split\n" " */\n" - " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = ''): self\n" + " public function chunkSplit(int $length = 76, string $separator = \"\\r\\n\"): static\n" " {\n" - " return new static(chunk_split($this->string, ...func_get_args()));\n" + " return new static(chunk_split($this->string, $length, $separator)); // @phpstan-ignore new.static\n" " }\n" "\n" - " public function chunk(int $splitLength = 1): ArrayObject\n" + " /**\n" + " * Convert a string to an array object of class \\Swoole\\ArrayObject.\n" + " *\n" + " * @param int $length Maximum length of the chunk.\n" + " * @see https://www.php.net/str_split\n" + " */\n" + " public function chunk(int $length = 1): ArrayObject\n" " {\n" - " return static::detectArrayType(str_split($this->string, ...func_get_args()));\n" + " return static::detectArrayType(str_split($this->string, $length));\n" " }\n" "\n" " public function toString(): string\n" @@ -753,57 +852,46 @@ static const char* swoole_library_source_core_multibyte_string_object = " return mb_strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function pos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function rpos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function ipos(string $needle, ?string $encoding = null)\n" + " public function ipos(string $needle, int $offset = 0, ?string $encoding = null): int|false\n" " {\n" - " return mb_stripos($this->string, ...func_get_args());\n" + " return mb_stripos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" " /**\n" - " * @return static\n" + " * @see https://www.php.net/mb_substr\n" " */\n" - " public function substr(int $offset, ?int $length = null, ?string $encoding = null)\n" + " public function substr(int $start, ?int $length = null, ?string $encoding = null): static\n" " {\n" - " return new static(mb_substr($this->string, ...func_get_args()));\n" + " return new static(mb_substr($this->string, $start, $length, $encoding)); // @phpstan-ignore new.static\n" " }\n" "\n" - " public function chunk(int $splitLength = 1, ?int $limit = null): ArrayObject\n" + " /**\n" + " * {@inheritDoc}\n" + " * @see https://www.php.net/mb_str_split\n" + " */\n" + " public function chunk(int $length = 1): ArrayObject\n" " {\n" - " return static::detectArrayType(mb_split($this->string, ...func_get_args()));\n" + " return static::detectArrayType(mb_str_split($this->string, $length));\n" " }\n" "}\n"; @@ -839,14 +927,9 @@ static const char* swoole_library_source_core_array_object = "\n" "namespace Swoole;\n" "\n" - "use ArrayAccess;\n" - "use Countable;\n" - "use Iterator;\n" - "use RuntimeException;\n" - "use Serializable;\n" "use Swoole\\Exception\\ArrayKeyNotExists;\n" "\n" - "class ArrayObject implements ArrayAccess, Serializable, Countable, Iterator\n" + "class ArrayObject implements \\ArrayAccess, \\Serializable, \\Countable, \\Iterator\n" "{\n" " /**\n" " * @var array\n" @@ -866,6 +949,21 @@ static const char* swoole_library_source_core_array_object = " return $this->array;\n" " }\n" "\n" + " public function __serialize(): array\n" + " {\n" + " return $this->array;\n" + " }\n" + "\n" + " public function __unserialize(array $data): void\n" + " {\n" + " $this->array = $data;\n" + " }\n" + "\n" + " public static function from(array $array = []): static\n" + " {\n" + " return new static($array); // @phpstan-ignore new.static\n" + " }\n" + "\n" " public function toArray(): array\n" " {\n" " return $this->array;\n" @@ -884,6 +982,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function current()\n" " {\n" " return current($this->array);\n" @@ -892,6 +991,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function key()\n" " {\n" " return key($this->array);\n" @@ -905,6 +1005,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function rewind()\n" " {\n" " return reset($this->array);\n" @@ -913,16 +1014,16 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function next()\n" " {\n" " return next($this->array);\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return ArrayObject|StringObject\n" " */\n" - " public function get($key)\n" + " public function get(mixed $key)\n" " {\n" " if (!$this->exists($key)) {\n" " throw new ArrayKeyNotExists($key);\n" @@ -931,11 +1032,9 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @param mixed $default\n" " * @return ArrayObject|StringObject\n" " */\n" - " public function getOr($key, $default = null)\n" + " public function getOr(mixed $key, mixed $default = null)\n" " {\n" " if (!$this->exists($key)) {\n" " return $default;\n" @@ -956,7 +1055,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function firstKey()\n" " {\n" @@ -964,7 +1063,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function lastKey()\n" " {\n" @@ -984,31 +1083,27 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function set($key, $value): self\n" + " public function set(mixed $key, mixed $value): self\n" " {\n" " $this->array[$key] = $value;\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return $this\n" " */\n" - " public function delete($key): self\n" + " public function delete(mixed $key): self\n" " {\n" " unset($this->array[$key]);\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function remove($value, bool $strict = true, bool $loop = false): self\n" + " public function remove(mixed $value, bool $strict = true, bool $loop = false): self\n" " {\n" " do {\n" " $key = $this->search($value, $strict);\n" @@ -1031,10 +1126,10 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @return null|mixed\n" + " * @return mixed|null\n" " */\n" - " public function offsetGet($key)\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetGet(mixed $key)\n" " {\n" " if (!array_key_exists($key, $this->array)) {\n" " return null;\n" @@ -1042,62 +1137,47 @@ static const char* swoole_library_source_core_array_object = " return $this->array[$key];\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" - " */\n" - " public function offsetSet($key, $value): void\n" + " public function offsetSet(mixed $key, mixed $value): void\n" " {\n" " $this->array[$key] = $value;\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function offsetUnset($key): void\n" + " public function offsetUnset(mixed $key): void\n" " {\n" " unset($this->array[$key]);\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return bool\n" " */\n" - " public function offsetExists($key)\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetExists(mixed $key)\n" " {\n" " return isset($this->array[$key]);\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function exists($key): bool\n" + " public function exists(mixed $key): bool\n" " {\n" " return array_key_exists($key, $this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $value\n" - " */\n" - " public function contains($value, bool $strict = true): bool\n" + " public function contains(mixed $value, bool $strict = true): bool\n" " {\n" " return in_array($value, $this->array, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function indexOf($value, bool $strict = true)\n" + " public function indexOf(mixed $value, bool $strict = true)\n" " {\n" " return $this->search($value, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function lastIndexOf($value, bool $strict = true)\n" + " public function lastIndexOf(mixed $value, bool $strict = true)\n" " {\n" " $array = $this->array;\n" " for (end($array); ($currentKey = key($array)) !== null; prev($array)) {\n" @@ -1113,29 +1193,24 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $needle\n" " * @return mixed\n" " */\n" - " public function search($needle, bool $strict = true)\n" + " public function search(mixed $needle, bool $strict = true)\n" " {\n" " return array_search($needle, $this->array, $strict);\n" " }\n" "\n" " public function join(string $glue = ''): StringObject\n" " {\n" - " return static::detectStringType(implode($glue, $this->array));\n" + " return self::detectStringType(implode($glue, $this->array));\n" " }\n" "\n" - " public function serialize(): StringObject\n" + " public function serialize(): string\n" " {\n" - " return static::detectStringType(serialize($this->array));\n" + " return serialize($this->array);\n" " }\n" "\n" - " /**\n" - " * @param string $string\n" - " * @return $this\n" - " */\n" - " public function unserialize($string): self\n" + " public function unserialize(string|\\Stringable|StringObject $string): self\n" " {\n" " $this->array = (array) unserialize((string) $string);\n" " return $this;\n" @@ -1158,19 +1233,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function push($value)\n" + " public function push(mixed $value)\n" " {\n" " return $this->pushBack($value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushFront($value)\n" + " public function pushFront(mixed $value)\n" " {\n" " return array_unshift($this->array, $value);\n" " }\n" @@ -1182,19 +1255,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushBack($value)\n" + " public function pushBack(mixed $value)\n" " {\n" " return array_push($this->array, $value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function insert(int $offset, $value): self\n" + " public function insert(int $offset, mixed $value): self\n" " {\n" " if (is_array($value) || is_object($value) || is_null($value)) {\n" " $value = [$value];\n" @@ -1227,14 +1298,9 @@ static const char* swoole_library_source_core_array_object = " return array_pop($this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $offset\n" - " * @param int $length\n" - " * @return static\n" - " */\n" - " public function slice($offset, int $length = null, bool $preserve_keys = false): self\n" + " public function slice(int $offset, ?int $length = null, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_slice($this->array, ...func_get_args()));\n" + " return new static(array_slice($this->array, $offset, $length, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1245,24 +1311,19 @@ static const char* swoole_library_source_core_array_object = " return static::detectType($this->array[array_rand($this->array, 1)]);\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function each(callable $fn): self\n" " {\n" - " if (array_walk($this->array, $fn) === false) {\n" - " throw new RuntimeException('array_walk() failed');\n" - " }\n" + " array_walk($this->array, $fn);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function map(callable $fn, ...$args): self\n" + " public function map(callable $fn, ...$args): static\n" " {\n" - " return new static(array_map($fn, $this->array, ...$args));\n" + " return new static(array_map($fn, $this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1276,76 +1337,54 @@ static const char* swoole_library_source_core_array_object = "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function keys(...$args): self\n" + " public function keys(...$args): static\n" " {\n" - " return new static(array_keys($this->array, ...$args));\n" + " return new static(array_keys($this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function values(): self\n" + " public function values(): static\n" " {\n" - " return new static(array_values($this->array));\n" + " return new static(array_values($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $column_key\n" - " * @param mixed $index\n" - " * @return static\n" - " */\n" - " public function column($column_key, $index = null): self\n" + " public function column(mixed $column_key, mixed $index = null): static\n" " {\n" - " return new static(array_column($this->array, $column_key, $index));\n" + " return new static(array_column($this->array, $column_key, $index)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function unique(int $sort_flags = SORT_STRING): self\n" + " public function unique(int $sort_flags = SORT_STRING): static\n" " {\n" - " return new static(array_unique($this->array, $sort_flags));\n" + " return new static(array_unique($this->array, $sort_flags)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function reverse(bool $preserve_keys = false): self\n" + " public function reverse(bool $preserve_keys = false): static\n" " {\n" - " return new static(array_reverse($this->array, $preserve_keys));\n" + " return new static(array_reverse($this->array, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunk(int $size, bool $preserve_keys = false): self\n" + " public function chunk(int $size, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_chunk($this->array, $size, $preserve_keys));\n" + " return new static(array_chunk($this->array, $size, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * Swap keys and values in an array.\n" - " * @return static\n" " */\n" - " public function flip(): self\n" + " public function flip(): static\n" " {\n" - " return new static(array_flip($this->array));\n" + " return new static(array_flip($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function filter(callable $fn, int $flag = 0): self\n" + " public function filter(callable $fn, int $flag = 0): static\n" " {\n" - " return new static(array_filter($this->array, $fn, $flag));\n" + " return new static(array_filter($this->array, $fn, $flag)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * | Function name | Sorts by | Maintains key association | Order of sort | Related functions |\n" " * | :---------------- | :------- | :-------------------------- | :-------------------------- | :---------------- |\n" - " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" + " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" " * | asort() | value | yes | low to high | arsort() |\n" " * | arsort() | value | yes | high to low | asort() |\n" " * | krsort() | key | yes | high to low | ksort() |\n" @@ -1353,11 +1392,11 @@ static const char* swoole_library_source_core_array_object = " * | natcasesort() | value | yes | natural, case insensitive | natsort() |\n" " * | natsort() | value | yes | natural | natcasesort() |\n" " * | rsort() | value | no | high to low | sort() |\n" - " * | shuffle() | value | no | random | array_rand() |\n" + " * | shuffle() | value | no | random | array_rand() |\n" " * | sort() | value | no | low to high | rsort() |\n" - " * | uasort() | value | yes | user defined | uksort() |\n" - " * | uksort() | key | yes | user defined | uasort() |\n" - " * | usort() | value | no | user defined | uasort() |\n" + " * | uasort() | value | yes | user defined | uksort() |\n" + " * | uksort() | key | yes | user defined | uasort() |\n" + " * | usort() | value | no | user defined | uasort() |\n" " */\n" "\n" " /**\n" @@ -1365,42 +1404,29 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function asort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (asort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('asort() failed');\n" - " }\n" + " asort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function arsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (arsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('arsort() failed');\n" - " }\n" + " arsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function krsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (krsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('krsort() failed');\n" - " }\n" + " krsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function ksort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (ksort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('ksort() failed');\n" - " }\n" + " ksort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" @@ -1409,8 +1435,8 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natcasesort(): self\n" " {\n" - " if (natcasesort($this->array) !== true) {\n" - " throw new RuntimeException('natcasesort() failed');\n" + " if (natcasesort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natcasesort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1420,8 +1446,8 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natsort(): self\n" " {\n" - " if (natsort($this->array) !== true) {\n" - " throw new RuntimeException('natsort() failed');\n" + " if (natsort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1431,72 +1457,51 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function rsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (rsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('rsort() failed');\n" + " if (rsort($this->array, $sort_flags) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('rsort() failed');\n" " }\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function shuffle(): self\n" " {\n" - " if (shuffle($this->array) !== true) {\n" - " throw new RuntimeException('shuffle() failed');\n" - " }\n" + " shuffle($this->array);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function sort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (sort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('sort() failed');\n" - " }\n" + " sort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uasort(callable $value_compare_func): self\n" " {\n" - " if (uasort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uasort() failed');\n" - " }\n" + " uasort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uksort(callable $value_compare_func): self\n" " {\n" - " if (uksort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uksort() failed');\n" - " }\n" + " uksort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function usort(callable $value_compare_func): self\n" " {\n" - " if (usort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('usort() failed');\n" - " }\n" + " usort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return ArrayObject|mixed|StringObject\n" " */\n" - " protected static function detectType($value)\n" + " protected static function detectType(mixed $value)\n" " {\n" " if (is_string($value)) {\n" " return static::detectStringType($value);\n" @@ -1512,12 +1517,9 @@ static const char* swoole_library_source_core_array_object = " return new StringObject($value);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " protected static function detectArrayType(array $value): self\n" + " protected static function detectArrayType(array $value): static\n" " {\n" - " return new static($value);\n" + " return new static($value); // @phpstan-ignore new.static\n" " }\n" "}\n"; @@ -1535,18 +1537,13 @@ static const char* swoole_library_source_core_object_proxy = "\n" "namespace Swoole;\n" "\n" - "use TypeError;\n" - "\n" "class ObjectProxy\n" "{\n" " /** @var object */\n" " protected $__object;\n" "\n" - " public function __construct($object)\n" + " public function __construct(object $object)\n" " {\n" - " if (!is_object($object)) {\n" - " throw new TypeError('Non-object given');\n" - " }\n" " $this->__object = $object;\n" " }\n" "\n" @@ -1602,16 +1599,13 @@ static const char* swoole_library_source_core_coroutine_wait_group = "\n" "namespace Swoole\\Coroutine;\n" "\n" - "use BadMethodCallException;\n" - "use InvalidArgumentException;\n" - "\n" "class WaitGroup\n" "{\n" - " protected $chan;\n" + " protected Channel $chan;\n" "\n" - " protected $count = 0;\n" + " protected int $count = 0;\n" "\n" - " protected $waiting = false;\n" + " protected bool $waiting = false;\n" "\n" " public function __construct(int $delta = 0)\n" " {\n" @@ -1624,11 +1618,11 @@ static const char* swoole_library_source_core_coroutine_wait_group = " public function add(int $delta = 1): void\n" " {\n" " if ($this->waiting) {\n" - " throw new BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" " }\n" " $count = $this->count + $delta;\n" " if ($count < 0) {\n" - " throw new InvalidArgumentException('WaitGroup misuse: negative counter');\n" + " throw new \\InvalidArgumentException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " }\n" @@ -1637,7 +1631,7 @@ static const char* swoole_library_source_core_coroutine_wait_group = " {\n" " $count = $this->count - 1;\n" " if ($count < 0) {\n" - " throw new BadMethodCallException('WaitGroup misuse: negative counter');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " if ($count === 0 && $this->waiting) {\n" @@ -1648,11 +1642,11 @@ static const char* swoole_library_source_core_coroutine_wait_group = " public function wait(float $timeout = -1): bool\n" " {\n" " if ($this->waiting) {\n" - " throw new BadMethodCallException('WaitGroup misuse: reused before previous wait has returned');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: reused before previous wait has returned');\n" " }\n" " if ($this->count > 0) {\n" " $this->waiting = true;\n" - " $done = $this->chan->pop($timeout);\n" + " $done = $this->chan->pop($timeout);\n" " $this->waiting = false;\n" " return $done;\n" " }\n" @@ -1679,13 +1673,11 @@ static const char* swoole_library_source_core_coroutine_server = "\n" "namespace Swoole\\Coroutine;\n" "\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine;\n" "use Swoole\\Coroutine\\Server\\Connection;\n" "use Swoole\\Exception;\n" "\n" - "/* compatibility constant */\n" - "define('SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE', method_exists(Socket::class, 'sslHandshake'));\n" - "\n" "class Server\n" "{\n" " /** @var string */\n" @@ -1709,7 +1701,7 @@ static const char* swoole_library_source_core_coroutine_server = " /** @var bool */\n" " protected $running = false;\n" "\n" - " /** @var null|callable */\n" + " /** @var callable|null */\n" " protected $fn;\n" "\n" " /** @var Socket */\n" @@ -1725,7 +1717,7 @@ static const char* swoole_library_source_core_coroutine_server = " if ($_host->contains('::')) {\n" " $this->type = AF_INET6;\n" " } elseif ($_host->startsWith('unix:/')) {\n" - " $host = $_host->substr(5)->__toString();\n" + " $host = $_host->substr(5)->__toString();\n" " $this->type = AF_UNIX;\n" " } else {\n" " $this->type = AF_INET;\n" @@ -1742,9 +1734,9 @@ static const char* swoole_library_source_core_coroutine_server = " if (!$socket->listen()) {\n" " throw new Exception('listen() failed', $socket->errCode);\n" " }\n" - " $this->port = $socket->getsockname()['port'] ?? 0;\n" - " $this->fd = $socket->fd;\n" - " $this->socket = $socket;\n" + " $this->port = $socket->getsockname()['port'] ?? 0;\n" + " $this->fd = $socket->fd;\n" + " $this->socket = $socket;\n" " $this->setting['open_ssl'] = $ssl;\n" " }\n" "\n" @@ -1777,13 +1769,13 @@ static const char* swoole_library_source_core_coroutine_server = " return false;\n" " }\n" "\n" - " while ($this->running) {\n" - " /** @var Socket $conn */\n" + " while ($this->running) { // @phpstan-ignore while.alwaysTrue\n" " $conn = null;\n" + " /** @var Socket $conn */\n" " $conn = $socket->accept();\n" - " if ($conn) {\n" + " if ($conn) { // @phpstan-ignore if.alwaysTrue\n" " $conn->setProtocol($this->setting);\n" - " if (SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE && $this->setting['open_ssl'] ?? false) {\n" + " if (!empty($this->setting[Constant::OPTION_OPEN_SSL])) {\n" " $fn = static function ($fn, $connection) {\n" " /* @var $connection Connection */\n" " if (!$connection->exportSocket()->sslHandshake()) {\n" @@ -1793,7 +1785,7 @@ static const char* swoole_library_source_core_coroutine_server = " };\n" " $arguments = [$this->fn, new Connection($conn)];\n" " } else {\n" - " $fn = $this->fn;\n" + " $fn = $this->fn;\n" " $arguments = [new Connection($conn)];\n" " }\n" " if (Coroutine::create($fn, ...$arguments) < 0) {\n" @@ -1816,7 +1808,7 @@ static const char* swoole_library_source_core_coroutine_server = " }\n" " }\n" "\n" - " return true;\n" + " return true; // @phpstan-ignore deadCode.unreachable\n" " }\n" "}\n"; @@ -1886,11 +1878,11 @@ static const char* swoole_library_source_core_coroutine_barrier = "\n" "class Barrier\n" "{\n" - " private $cid = -1;\n" + " private int $cid = -1;\n" "\n" " private $timer = -1;\n" "\n" - " private static $cancel_list = [];\n" + " private static array $cancel_list = [];\n" "\n" " public function __destruct()\n" " {\n" @@ -1908,20 +1900,20 @@ static const char* swoole_library_source_core_coroutine_barrier = " }\n" " }\n" "\n" - " public static function make()\n" + " public static function make(): self\n" " {\n" - " return new static();\n" + " return new self();\n" " }\n" "\n" " /**\n" - " * @throws Exception\n" + " * @param-out null $barrier\n" " */\n" - " public static function wait(Barrier &$barrier, float $timeout = -1)\n" + " public static function wait(Barrier &$barrier, float $timeout = -1): void\n" " {\n" " if ($barrier->cid !== -1) {\n" " throw new Exception('The barrier is waiting, cannot wait again.');\n" " }\n" - " $cid = Coroutine::getCid();\n" + " $cid = Coroutine::getCid();\n" " $barrier->cid = $cid;\n" " if ($timeout > 0 && ($timeout_ms = (int) ($timeout * 1000)) > 0) {\n" " $barrier->timer = Timer::after($timeout_ms, function () use ($cid) {\n" @@ -1938,7 +1930,7 @@ static const char* swoole_library_source_core_coroutine_barrier = " }\n" "}\n"; -static const char* swoole_library_source_core_coroutine_http_functions = +static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" "/**\n" " * This file is part of Swoole.\n" @@ -1952,16 +1944,89 @@ static const char* swoole_library_source_core_coroutine_http_functions = "\n" "namespace Swoole\\Coroutine\\Http;\n" "\n" - "use Swoole\\Coroutine\\Http\\Client\\Exception;\n" - "\n" - "/**\n" - " * @param mixed $data\n" - " * @throws Exception\n" - " * @return mixed\n" - " */\n" - "function request(string $url, string $method, $data = null, array $options = null, array $headers = null, array $cookies = null)\n" + "class ClientProxy\n" "{\n" - " $info = parse_url($url);\n" + " private array $headers;\n" + "\n" + " private array $cookies;\n" + "\n" + " public function __construct(private string $body, private int $statusCode, ?array $headers, ?array $cookies)\n" + " {\n" + " $this->headers = $headers ?? [];\n" + " $this->cookies = $cookies ?? [];\n" + " }\n" + "\n" + " public function getBody(): string\n" + " {\n" + " return $this->body;\n" + " }\n" + "\n" + " public function getStatusCode(): int\n" + " {\n" + " return $this->statusCode;\n" + " }\n" + "\n" + " public function getHeaders(): array\n" + " {\n" + " return $this->headers;\n" + " }\n" + "\n" + " public function getCookies(): array\n" + " {\n" + " return $this->cookies;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_http_functions = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Coroutine\\Http;\n" + "\n" + "use Swoole\\Coroutine\\Http\\Client\\Exception;\n" + "\n" + "/**\n" + " * @throws Exception\n" + " */\n" + "function request(\n" + " string $url,\n" + " string $method,\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null,\n" + "): ClientProxy {\n" + " $driver = swoole_library_get_option('http_client_driver');\n" + " return match ($driver) {\n" + " 'curl' => request_with_curl($url, $method, $data, $options, $headers, $cookies),\n" + " 'stream' => request_with_stream($url, $method, $data, $options, $headers, $cookies),\n" + " default => request_with_http_client($url, $method, $data, $options, $headers, $cookies),\n" + " };\n" + "}\n" + "\n" + "/**\n" + " * @throws Exception\n" + " */\n" + "function request_with_http_client(\n" + " string $url,\n" + " string $method,\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null,\n" + "): ClientProxy {\n" + " $info = parse_url($url);\n" + " if (empty($info['scheme'])) {\n" + " throw new Exception('The URL given is illegal [no scheme]');\n" + " }\n" " if ($info['scheme'] == 'http') {\n" " $client = new Client($info['host'], swoole_array_default_value($info, 'port', 80), false);\n" " } elseif ($info['scheme'] == 'https') {\n" @@ -1973,40 +2038,154 @@ static const char* swoole_library_source_core_coroutine_http_functions = " if ($data) {\n" " $client->setData($data);\n" " }\n" - " if (is_array($options)) {\n" - " $client->set($options);\n" - " }\n" - " if (is_array($headers)) {\n" - " $client->setHeaders($headers);\n" - " }\n" - " if (is_array($cookies)) {\n" - " $client->setCookies($cookies);\n" - " }\n" + " $client->set($options ?: []);\n" + " $client->setHeaders($headers ?: []);\n" + " $client->setCookies($cookies ?: []);\n" " $request_url = swoole_array_default_value($info, 'path', '/');\n" " if (!empty($info['query'])) {\n" " $request_url .= '?' . $info['query'];\n" " }\n" " if ($client->execute($request_url)) {\n" - " return $client;\n" + " return new ClientProxy(\n" + " $client->getBody(),\n" + " $client->getStatusCode(),\n" + " $client->getHeaders() ?: [],\n" + " $client->getCookies() ?: []\n" + " );\n" " }\n" - " return false;\n" + " throw new Exception($client->errMsg, $client->errCode);\n" + "}\n" + "\n" + "/**\n" + " * @throws Exception\n" + " */\n" + "function request_with_curl(\n" + " string $url,\n" + " string $method,\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null,\n" + "): ClientProxy {\n" + " $ch = curl_init($url);\n" + " if (empty($ch)) {\n" + " throw new Exception('failed to curl_init');\n" + " }\n" + " curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n" + " curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));\n" + " $responseHeaders = $responseCookies = [];\n" + " curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$responseHeaders, &$responseCookies) {\n" + " $len = strlen($header);\n" + " $header = explode(':', $header, 2);\n" + " if (count($header) < 2) {\n" + " return $len;\n" + " }\n" + " $headerKey = strtolower(trim($header[0]));\n" + " if ($headerKey == 'set-cookie') {\n" + " [$k, $v] = explode('=', $header[1]);\n" + " $responseCookies[$k] = $v;\n" + " } else {\n" + " $responseHeaders[$headerKey][] = trim($header[1]);\n" + " }\n" + " return $len;\n" + " });\n" + " if ($data) {\n" + " curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\n" + " }\n" + " if ($headers) {\n" + " curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);\n" + " }\n" + " if ($cookies) {\n" + " $cookie_str = '';\n" + " foreach ($cookies as $k => $v) {\n" + " $cookie_str .= \"{$k}={$v}; \";\n" + " }\n" + " curl_setopt($ch, CURLOPT_COOKIE, $cookie_str);\n" + " }\n" + " if (isset($options['timeout'])) {\n" + " if (is_float($options['timeout'])) {\n" + " curl_setopt($ch, CURLOPT_TIMEOUT_MS, intval($options['timeout'] * 1000));\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['timeout'] * 1000));\n" + " } else {\n" + " curl_setopt($ch, CURLOPT_TIMEOUT, intval($options['timeout']));\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['timeout']));\n" + " }\n" + " }\n" + " if (isset($options['connect_timeout'])) {\n" + " if (is_float($options['connect_timeout'])) {\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['connect_timeout'] * 1000));\n" + " } else {\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['connect_timeout']));\n" + " }\n" + " }\n" + " $body = curl_exec($ch);\n" + " if ($body !== false) {\n" + " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_RESPONSE_CODE), $responseHeaders, $responseCookies);\n" + " }\n" + " throw new Exception(curl_error($ch), curl_errno($ch));\n" + "}\n" + "\n" + "/**\n" + " * @throws Exception\n" + " */\n" + "function request_with_stream(\n" + " string $url,\n" + " string $method,\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null,\n" + "): ClientProxy {\n" + " $stream_options = [\n" + " 'http' => [\n" + " 'method' => $method,\n" + " ],\n" + " ];\n" + " $headerStr = '';\n" + " if ($headers) {\n" + " foreach ($headers as $k => $v) {\n" + " $headerStr .= \"{$k}: {$v}\\r\\n\";\n" + " }\n" + " }\n" + " if ($cookies) {\n" + " foreach ($cookies as $k => $v) {\n" + " $headerStr .= \"Cookie: {$k}={$v}\\r\\n\";\n" + " }\n" + " }\n" + " if (isset($options['timeout'])) {\n" + " $stream_options['http']['timeout'] = intval($options['timeout']);\n" + " }\n" + " if ($data) {\n" + " if (is_array($data)) {\n" + " $headerStr .= \"Content-type: application/x-www-form-urlencoded\\r\\n\";\n" + " $stream_options['http']['content'] = http_build_query($data);\n" + " } else {\n" + " $stream_options['http']['content'] = strval($data);\n" + " }\n" + " }\n" + " if ($headerStr) {\n" + " $stream_options['http']['header'] = $headerStr;\n" + " }\n" + " $body = file_get_contents($url, false, stream_context_create($stream_options));\n" + " if ($body) {\n" + " return new ClientProxy($body, 200, [], []);\n" + " }\n" + " $error = error_get_last();\n" + " throw new Exception($error['message']);\n" "}\n" "\n" "/**\n" - " * @param mixed $data\n" " * @throws Exception\n" - " * @return Client|false|mixed\n" " */\n" - "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null)\n" + "function post(string $url, mixed $data, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'POST', $data, $options, $headers, $cookies);\n" "}\n" "\n" "/**\n" " * @throws Exception\n" - " * @return Client|false|mixed\n" " */\n" - "function get(string $url, array $options = null, array $headers = null, array $cookies = null)\n" + "function get(string $url, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'GET', null, $options, $headers, $cookies);\n" "}\n"; @@ -2025,35 +2204,25 @@ static const char* swoole_library_source_core_connection_pool = "\n" "namespace Swoole;\n" "\n" - "use RuntimeException;\n" "use Swoole\\Coroutine\\Channel;\n" - "use Throwable;\n" "\n" "class ConnectionPool\n" "{\n" " public const DEFAULT_SIZE = 64;\n" "\n" - " /** @var Channel */\n" - " protected $pool;\n" + " protected ?Channel $pool;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $size;\n" - "\n" - " /** @var int */\n" - " protected $num;\n" + " protected int $size;\n" "\n" - " /** @var null|string */\n" - " protected $proxy;\n" + " protected int $num = 0;\n" "\n" - " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, ?string $proxy = null)\n" + " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, protected ?string $proxy = null)\n" " {\n" - " $this->pool = new Channel($this->size = $size);\n" + " $this->pool = new Channel($this->size = $size);\n" " $this->constructor = $constructor;\n" - " $this->num = 0;\n" - " $this->proxy = $proxy;\n" " }\n" "\n" " public function fill(): void\n" @@ -2063,10 +2232,16 @@ static const char* swoole_library_source_core_connection_pool = " }\n" " }\n" "\n" + " /**\n" + " * Get a connection from the pool.\n" + " *\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\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" " public function get(float $timeout = -1)\n" " {\n" " if ($this->pool === null) {\n" - " throw new RuntimeException('Pool has been closed');\n" + " throw new \\RuntimeException('Pool has been closed');\n" " }\n" " if ($this->pool->isEmpty() && $this->num < $this->size) {\n" " $this->make();\n" @@ -2092,7 +2267,7 @@ static const char* swoole_library_source_core_connection_pool = " {\n" " $this->pool->close();\n" " $this->pool = null;\n" - " $this->num = 0;\n" + " $this->num = 0;\n" " }\n" "\n" " protected function make(): void\n" @@ -2103,9 +2278,9 @@ static const char* swoole_library_source_core_connection_pool = " $connection = new $this->proxy($this->constructor);\n" " } else {\n" " $constructor = $this->constructor;\n" - " $connection = $constructor();\n" + " $connection = $constructor();\n" " }\n" - " } catch (Throwable $throwable) {\n" + " } catch (\\Throwable $throwable) {\n" " $this->num--;\n" " throw $throwable;\n" " }\n" @@ -2127,13 +2302,11 @@ static const char* swoole_library_source_core_database_object_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use Error;\n" - "\n" "class ObjectProxy extends \\Swoole\\ObjectProxy\n" "{\n" - " public function __clone()\n" + " final public function __clone(): void\n" " {\n" - " throw new Error('Trying to clone an uncloneable database proxy object');\n" + " throw new \\Error('Trying to clone an uncloneable database proxy object');\n" " }\n" "}\n"; @@ -2153,36 +2326,28 @@ static const char* swoole_library_source_core_database_mysqli_config = "\n" "class MysqliConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket = '';\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -2193,9 +2358,9 @@ static const char* swoole_library_source_core_database_mysqli_config = " return $this->port;\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -2280,9 +2445,7 @@ static const char* swoole_library_source_core_database_mysqli_exception = "\n" "namespace Swoole\\Database;\n" "\n" - "use Exception;\n" - "\n" - "class MysqliException extends Exception\n" + "class MysqliException extends \\Exception\n" "{\n" "}\n"; @@ -2304,19 +2467,15 @@ static const char* swoole_library_source_core_database_mysqli_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method mysqli|MysqliProxy get()\n" + " * @method \\mysqli|MysqliProxy get()\n" " * @method void put(mysqli|MysqliProxy $connection)\n" " */\n" "class MysqliPool extends ConnectionPool\n" "{\n" - " /** @var MysqliConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" - " $mysqli = new mysqli();\n" + " $mysqli = new \\mysqli();\n" " foreach ($this->config->getOptions() as $option => $value) {\n" " $mysqli->set_opt($option, $value);\n" " }\n" @@ -2331,6 +2490,7 @@ static const char* swoole_library_source_core_database_mysqli_pool = " if ($mysqli->connect_errno) {\n" " throw new MysqliException($mysqli->connect_error, $mysqli->connect_errno);\n" " }\n" + " $mysqli->set_charset($this->config->getCharset());\n" " return $mysqli;\n" " }, $size, MysqliProxy::class);\n" " }\n" @@ -2350,8 +2510,9 @@ static const char* swoole_library_source_core_database_mysqli_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "\n" + "/**\n" + " * @method \\mysqli __getObject()\n" + " */\n" "class MysqliProxy extends ObjectProxy\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" @@ -2362,23 +2523,19 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " /** @var mysqli */\n" + " /** @var \\mysqli */\n" " protected $__object;\n" "\n" - " /** @var string */\n" - " protected $charsetContext;\n" + " protected string $charsetContext;\n" "\n" - " /** @var null|array */\n" - " protected $setOptContext;\n" + " protected array $setOptContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $changeUserContext;\n" + " protected array $changeUserContext;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $round = 0;\n" + " protected int $round = 0;\n" "\n" " public function __construct(callable $constructor)\n" " {\n" @@ -2424,15 +2581,13 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " parent::__construct($constructor());\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->charsetContext) {\n" + " if (!empty($this->charsetContext)) {\n" " $this->__object->set_charset($this->charsetContext);\n" " }\n" - " if ($this->setOptContext) {\n" - " foreach ($this->setOptContext as $opt => $val) {\n" - " $this->__object->set_opt($opt, $val);\n" - " }\n" + " foreach ($this->setOptContext as $opt => $val) {\n" + " $this->__object->set_opt($opt, $val);\n" " }\n" - " if ($this->changeUserContext) {\n" + " if (!empty($this->changeUserContext)) {\n" " $this->__object->change_user(...$this->changeUserContext);\n" " }\n" " }\n" @@ -2454,7 +2609,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " return $this->__object->set_charset($charset);\n" " }\n" "\n" - " public function change_user(string $user, string $password, string $database): bool\n" + " public function change_user(string $user, string $password, ?string $database): bool\n" " {\n" " $this->changeUserContext = [$user, $password, $database];\n" " return $this->__object->change_user($user, $password, $database);\n" @@ -2475,39 +2630,30 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "use mysqli_stmt;\n" - "\n" "class MysqliStatementProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\n" "\n" - " /** @var mysqli_stmt */\n" + " /** @var \\mysqli_stmt */\n" " protected $__object;\n" "\n" - " /** @var null|string */\n" - " protected $queryString;\n" + " protected ?string $queryString;\n" "\n" - " /** @var null|array */\n" - " protected $attrSetContext;\n" + " protected array $attrSetContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindResultContext;\n" + " protected array $bindResultContext;\n" "\n" - " /** @var Mysqli|MysqliProxy */\n" - " protected $parent;\n" + " protected MysqliProxy $parent;\n" "\n" - " /** @var int */\n" - " protected $parentRound;\n" + " protected int $parentRound;\n" "\n" - " public function __construct(mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" + " public function __construct(\\mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->queryString = $queryString;\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" @@ -2528,21 +2674,19 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" - " $parent = $this->parent->__getObject();\n" + " $parent = $this->parent->__getObject();\n" " $this->__object = $this->queryString ? @$parent->prepare($this->queryString) : @$parent->stmt_init();\n" " if ($this->__object === false) {\n" " throw new MysqliException($parent->error, $parent->errno);\n" " }\n" - " if ($this->bindParamContext) {\n" + " if (!empty($this->bindParamContext)) {\n" " $this->__object->bind_param($this->bindParamContext[0], ...$this->bindParamContext[1]);\n" " }\n" - " if ($this->bindResultContext) {\n" + " if (!empty($this->bindResultContext)) {\n" " $this->__object->bind_result($this->bindResultContext);\n" " }\n" - " if ($this->attrSetContext) {\n" - " foreach ($this->attrSetContext as $attr => $value) {\n" - " $this->__object->attr_set($attr, $value);\n" - " }\n" + " foreach ($this->attrSetContext as $attr => $value) {\n" + " $this->__object->attr_set($attr, $value);\n" " }\n" " continue;\n" " }\n" @@ -2574,6 +2718,93 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " }\n" "}\n"; +static const char* swoole_library_source_core_database_detects_lost_connections = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Database;\n" + "\n" + "class DetectsLostConnections\n" + "{\n" + " /**\n" + " * @var array\n" + " */\n" + " private const ERROR_MESSAGES = [\n" + " 'server has gone away',\n" + " 'no connection to the server',\n" + " 'Lost connection',\n" + " 'is dead or not enabled',\n" + " 'Error while sending',\n" + " 'decryption failed or bad record mac',\n" + " 'server closed the connection unexpectedly',\n" + " 'SSL connection has been closed unexpectedly',\n" + " 'Error writing data to the connection',\n" + " 'Resource deadlock avoided',\n" + " 'Transaction() on null',\n" + " 'child connection forced to terminate due to client_idle_limit',\n" + " 'query_wait_timeout',\n" + " 'reset by peer',\n" + " 'Physical connection is not usable',\n" + " 'TCP Provider: Error code 0x68',\n" + " 'ORA-03113',\n" + " 'ORA-03114',\n" + " 'Packets out of order. Expected',\n" + " 'Adaptive Server connection failed',\n" + " 'Communication link failure',\n" + " 'connection is no longer usable',\n" + " 'Login timeout expired',\n" + " 'SQLSTATE[HY000] [2002] Connection refused',\n" + " 'running with the --read-only option so it cannot execute this statement',\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" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',\n" + " 'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',\n" + " 'Temporary failure in name resolution',\n" + " 'SQLSTATE[08S01]: Communication link failure',\n" + " 'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',\n" + " 'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',\n" + " 'SQLSTATE[08006] [7] could not translate host name',\n" + " 'TCP Provider: Error code 0x274C',\n" + " 'SQLSTATE[HY000] [2002] No such file or directory',\n" + " 'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',\n" + " 'Unknown $curl_error_code: 77',\n" + " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" + " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" + " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" + " 'Broken pipe',\n" + " // PDO::prepare(): Send of 77 bytes failed with errno=110 Operation timed out\n" + " // SSL: Handshake timed out\n" + " // SSL: Operation timed out\n" + " // SSL: Connection timed out\n" + " // SQLSTATE[HY000] [2002] Connection timed out\n" + " 'timed out',\n" + " 'Error reading result',\n" + " ];\n" + "\n" + " public static function causedByLostConnection(\\Throwable $e): bool\n" + " {\n" + " $message = $e->getMessage();\n" + " foreach (self::ERROR_MESSAGES as $needle) {\n" + " if (mb_strpos($message, $needle) !== false) {\n" + " return true;\n" + " }\n" + " }\n" + "\n" + " return false;\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_database_pdo_config = "\n" "/**\n" @@ -2592,32 +2823,23 @@ static const char* swoole_library_source_core_database_pdo_config = "{\n" " public const DRIVER_MYSQL = 'mysql';\n" "\n" - " /** @var string */\n" - " protected $driver = self::DRIVER_MYSQL;\n" + " protected string $driver = self::DRIVER_MYSQL;\n" "\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket;\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getDriver(): string\n" " {\n" @@ -2635,7 +2857,7 @@ static const char* swoole_library_source_core_database_pdo_config = " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -2648,12 +2870,12 @@ static const char* swoole_library_source_core_database_pdo_config = "\n" " public function hasUnixSocket(): bool\n" " {\n" - " return isset($this->unixSocket);\n" + " return !empty($this->unixSocket);\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -2728,7 +2950,7 @@ static const char* swoole_library_source_core_database_pdo_config = " *\n" " * @return string[]\n" " */\n" - " public static function getAvailableDrivers()\n" + " public static function getAvailableDrivers(): array\n" " {\n" " return [\n" " self::DRIVER_MYSQL,\n" @@ -2754,36 +2976,80 @@ static const char* swoole_library_source_core_database_pdo_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method PDO|PDOProxy get()\n" " * @method void put(PDO|PDOProxy $connection)\n" " */\n" "class PDOPool extends ConnectionPool\n" "{\n" - " /** @var int */\n" - " protected $size = 64;\n" - "\n" - " /** @var PDOConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" - " return new PDO(\n" - " \"{$this->config->getDriver()}:\" .\n" - " (\n" - " $this->config->hasUnixSocket() ?\n" - " \"unix_socket={$this->config->getUnixSocket()};\" :\n" - " \"host={$this->config->getHost()};\" . \"port={$this->config->getPort()};\"\n" - " ) .\n" - " \"dbname={$this->config->getDbname()};\" .\n" - " \"charset={$this->config->getCharset()}\",\n" - " $this->config->getUsername(),\n" - " $this->config->getPassword(),\n" - " $this->config->getOptions()\n" - " );\n" + " $driver = $this->config->getDriver();\n" + " if ($driver === 'sqlite') {\n" + " return new \\PDO($this->createDSN('sqlite'));\n" + " }\n" + "\n" + " return new \\PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\n" " }, $size, PDOProxy::class);\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" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return PDOProxy|false Returns a PDOProxy object from the pool, or false if the pool is full and the timeout is reached.\n" + " * {@inheritDoc}\n" + " */\n" + " public function get(float $timeout = -1)\n" + " {\n" + " /* @var \\Swoole\\Database\\PDOProxy|false $pdo */\n" + " $pdo = parent::get($timeout);\n" + " if ($pdo === false) {\n" + " return false;\n" + " }\n" + "\n" + " $pdo->reset();\n" + "\n" + " return $pdo;\n" + " }\n" + "\n" + " /**\n" + " * @purpose create DSN\n" + " * @throws \\Exception\n" + " */\n" + " private function createDSN(string $driver): string\n" + " {\n" + " switch ($driver) {\n" + " case 'mysql':\n" + " if ($this->config->hasUnixSocket()) {\n" + " $dsn = \"mysql:unix_socket={$this->config->getUnixSocket()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " } else {\n" + " $dsn = \"mysql:host={$this->config->getHost()};port={$this->config->getPort()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " }\n" + " break;\n" + " case 'pgsql':\n" + " $dsn = 'pgsql:host=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . \";port={$this->config->getPort()};dbname={$this->config->getDbname()}\";\n" + " break;\n" + " case 'oci':\n" + " $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset();\n" + " break;\n" + " case 'sqlite':\n" + " // There are three types of SQLite databases: databases on disk, databases in memory, and temporary\n" + " // databases (which are deleted when the connections are closed). It doesn't make sense to use\n" + " // connection pool for the latter two types of databases, because each connection connects to a\n" + " //different in-memory or temporary SQLite database.\n" + " if ($this->config->getDbname() === '') {\n" + " throw new \\Exception('Connection pool in Swoole does not support temporary SQLite databases.');\n" + " }\n" + " if ($this->config->getDbname() === ':memory:') {\n" + " throw new \\Exception('Connection pool in Swoole does not support creating SQLite databases in memory.');\n" + " }\n" + " $dsn = 'sqlite:' . $this->config->getDbname();\n" + " break;\n" + " default:\n" + " throw new \\Exception('Unsupported Database Driver:' . $driver);\n" + " }\n" + " return $dsn;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_database_pdo_proxy = @@ -2800,72 +3066,55 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "\n" + "/**\n" + " * @method \\PDO __getObject()\n" + " */\n" "class PDOProxy extends ObjectProxy\n" "{\n" - " public const IO_METHOD_REGEX = '/^query|prepare|exec|beginTransaction|commit|rollback$/i';\n" - "\n" - " public const IO_ERRORS = [\n" - " 2002, // MYSQLND_CR_CONNECTION_ERROR\n" - " 2006, // MYSQLND_CR_SERVER_GONE_ERROR\n" - " 2013, // MYSQLND_CR_SERVER_LOST\n" - " ];\n" - "\n" - " /** @var PDO */\n" + " /** @var \\PDO */\n" " protected $__object;\n" "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" + " protected array $setAttributeContext = [];\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $round = 0;\n" + " protected int $round = 0;\n" + "\n" + " protected int $inTransaction = 0;\n" "\n" " public function __construct(callable $constructor)\n" " {\n" " parent::__construct($constructor());\n" - " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n" " $this->constructor = $constructor;\n" " }\n" "\n" " public function __call(string $name, array $arguments)\n" " {\n" - " for ($n = 3; $n--;) {\n" - " $ret = @$this->__object->{$name}(...$arguments);\n" - " if ($ret === false) {\n" - " /* non-IO method */\n" - " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" - " break;\n" - " }\n" - " $errorInfo = $this->__object->errorInfo();\n" - " /* no more chances or non-IO failures */\n" - " if (\n" - " !in_array($errorInfo[1], static::IO_ERRORS, true)\n" - " || $n === 0\n" - " || $this->__object->inTransaction()\n" - " ) {\n" - " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" - " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" - " /* no error info, just return false */\n" - " break;\n" - " }\n" + " try {\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } catch (\\PDOException $e) {\n" + " if (!$this->__object->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " $this->reconnect();\n" - " continue;\n" - " }\n" - " if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) {\n" - " $ret = new PDOStatementProxy($ret, $this);\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" " }\n" - " break;\n" " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" + "\n" + " if (strcasecmp($name, 'beginTransaction') === 0) {\n" + " $this->inTransaction++;\n" + " }\n" + "\n" + " if ((strcasecmp($name, 'commit') === 0 || strcasecmp($name, 'rollback') === 0) && $this->inTransaction > 0) {\n" + " $this->inTransaction--;\n" + " }\n" + "\n" + " if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) {\n" + " $ret = new PDOStatementProxy($ret, $this);\n" + " }\n" + "\n" " return $ret;\n" " }\n" "\n" @@ -2878,12 +3127,11 @@ static const char* swoole_library_source_core_database_pdo_proxy = " {\n" " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->setAttributeContext) {\n" - " foreach ($this->setAttributeContext as $attribute => $value) {\n" - " $this->__object->setAttribute($attribute, $value);\n" - " }\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" " }\n" " }\n" "\n" @@ -2895,7 +3143,12 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" " public function inTransaction(): bool\n" " {\n" - " return $this->__object->inTransaction();\n" + " return $this->inTransaction > 0;\n" + " }\n" + "\n" + " public function reset(): void\n" + " {\n" + " $this->inTransaction = 0;\n" " }\n" "}\n"; @@ -2913,109 +3166,72 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "use PDOStatement;\n" - "\n" + "/**\n" + " * The proxy class for PHP class PDOStatement.\n" + " *\n" + " * @see https://www.php.net/PDOStatement The PDOStatement class\n" + " */\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " /** @var PDOStatement */\n" + " /** @var \\PDOStatement */\n" " protected $__object;\n" "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" + " protected array $setAttributeContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $setFetchModeContext;\n" + " protected array $setFetchModeContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindColumnContext;\n" + " protected array $bindColumnContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindValueContext;\n" + " protected array $bindValueContext = [];\n" "\n" - " /** @var PDO|PDOProxy */\n" - " protected $parent;\n" + " protected PDOProxy $parent;\n" "\n" " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(PDOStatement $object, PDOProxy $parent)\n" + " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" " {\n" " parent::__construct($object);\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" " public function __call(string $name, array $arguments)\n" " {\n" - " for ($n = 3; $n--;) {\n" - " $ret = @$this->__object->{$name}(...$arguments);\n" - " if ($ret === false) {\n" - " /* no IO */\n" - " if (strtolower($name) !== 'execute') {\n" - " break;\n" - " }\n" - " /* no more chances or non-IO failures or in transaction */\n" - " if (\n" - " !in_array($this->__object->errorInfo()[1], $this->parent::IO_ERRORS, true)\n" - " || $n === 0\n" - " || $this->parent->inTransaction()\n" - " ) {\n" - " $errorInfo = $this->__object->errorInfo();\n" - "\n" - " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" - " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" - " /* no error info, just return false */\n" - " break;\n" - " }\n" + " try {\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } catch (\\PDOException $e) {\n" + " if (!$this->parent->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " if ($this->parent->getRound() === $this->parentRound) {\n" " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" - " $parent = $this->parent->__getObject();\n" + " $parent = $this->parent->__getObject();\n" " $this->__object = $parent->prepare($this->__object->queryString);\n" - " if ($this->__object === false) {\n" - " $errorInfo = $parent->errorInfo();\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" - " if ($this->setAttributeContext) {\n" - " foreach ($this->setAttributeContext as $attribute => $value) {\n" - " $this->__object->setAttribute($attribute, $value);\n" - " }\n" + "\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" " }\n" - " if ($this->setFetchModeContext) {\n" + " if (!empty($this->setFetchModeContext)) {\n" " $this->__object->setFetchMode(...$this->setFetchModeContext);\n" " }\n" - " if ($this->bindParamContext) {\n" - " foreach ($this->bindParamContext as $param => $item) {\n" - " $this->__object->bindParam($param, ...$item);\n" - " }\n" + " foreach ($this->bindParamContext as $param => $item) {\n" + " $this->__object->bindParam($param, ...$item);\n" " }\n" - " if ($this->bindColumnContext) {\n" - " foreach ($this->bindColumnContext as $column => $item) {\n" - " $this->__object->bindColumn($column, ...$item);\n" - " }\n" + " foreach ($this->bindColumnContext as $column => $item) {\n" + " $this->__object->bindColumn($column, ...$item);\n" " }\n" - " if ($this->bindValueContext) {\n" - " foreach ($this->bindValueContext as $value => $item) {\n" - " $this->__object->bindParam($value, ...$item);\n" - " }\n" + " foreach ($this->bindValueContext as $value => $item) {\n" + " $this->__object->bindParam($value, ...$item);\n" " }\n" - " continue;\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" " }\n" - " break;\n" " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" + "\n" " return $ret;\n" " }\n" "\n" @@ -3025,13 +3241,18 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->setAttribute($attribute, $value);\n" " }\n" "\n" - " public function setFetchMode(int $mode, ...$args): bool\n" + " /**\n" + " * Set the default fetch mode for this statement.\n" + " *\n" + " * @see https://www.php.net/manual/en/pdostatement.setfetchmode.php\n" + " */\n" + " public function setFetchMode(int $mode, ...$params): bool\n" " {\n" " $this->setFetchModeContext = func_get_args();\n" " return $this->__object->setFetchMode(...$this->setFetchModeContext);\n" " }\n" "\n" - " public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = 0, $driver_options = null): bool\n" + " public function bindParam($parameter, &$variable, $data_type = \\PDO::PARAM_STR, $length = 0, $driver_options = null): bool\n" " {\n" " $this->bindParamContext[$parameter] = [$variable, $data_type, $length, $driver_options];\n" " return $this->__object->bindParam($parameter, $variable, $data_type, $length, $driver_options);\n" @@ -3043,7 +3264,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\n" " }\n" "\n" - " public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR): bool\n" + " public function bindValue($parameter, $value, $data_type = \\PDO::PARAM_STR): bool\n" " {\n" " $this->bindValueContext[$parameter] = [$value, $data_type];\n" " return $this->__object->bindValue($parameter, $value, $data_type);\n" @@ -3066,36 +3287,33 @@ static const char* swoole_library_source_core_database_redis_config = "\n" "class RedisConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 6379;\n" + " protected int $port = 6379;\n" "\n" - " /** @var float */\n" - " protected $timeout = 0.0;\n" + " protected float $timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $reserved = '';\n" + " protected string $reserved = '';\n" "\n" - " /** @var int */\n" - " protected $retry_interval = 0;\n" + " protected int $retry_interval = 0;\n" "\n" - " /** @var float */\n" - " protected $read_timeout = 0.0;\n" + " protected float $read_timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $auth = '';\n" + " protected string $auth = '';\n" "\n" - " /** @var int */\n" - " protected $dbIndex = 0;\n" + " protected int $dbIndex = 0;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $options = [];\n" "\n" - " public function getHost()\n" + " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -3177,6 +3395,36 @@ static const char* swoole_library_source_core_database_redis_config = " $this->dbIndex = $dbIndex;\n" " return $this;\n" " }\n" + "\n" + " /**\n" + " * Add a configurable option.\n" + " */\n" + " public function withOption(int $option, mixed $value): self\n" + " {\n" + " $this->options[$option] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Add/override configurable options.\n" + " *\n" + " * @param array $options\n" + " */\n" + " public function setOptions(array $options): self\n" + " {\n" + " $this->options = $options;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Get configurable options.\n" + " *\n" + " * @return array\n" + " */\n" + " public function getOptions(): array\n" + " {\n" + " return $this->options;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_database_redis_pool = @@ -3197,19 +3445,15 @@ static const char* swoole_library_source_core_database_redis_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method Redis get()\n" + " * @method \\Redis get()\n" " * @method void put(Redis $connection)\n" " */\n" "class RedisPool extends ConnectionPool\n" "{\n" - " /** @var RedisConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" - " $redis = new Redis();\n" + " $redis = new \\Redis();\n" " /* Compatible with different versions of Redis extension as much as possible */\n" " $arguments = [\n" " $this->config->getHost(),\n" @@ -3233,6 +3477,12 @@ static const char* swoole_library_source_core_database_redis_pool = " if ($this->config->getDbIndex() !== 0) {\n" " $redis->select($this->config->getDbIndex());\n" " }\n" + "\n" + " /* Set Redis options. */\n" + " foreach ($this->config->getOptions() as $key => $value) {\n" + " $redis->setOption($key, $value);\n" + " }\n" + "\n" " return $redis;\n" " }, $size);\n" " }\n" @@ -3377,66 +3627,66 @@ static const char* swoole_library_source_core_http_status = " public const NETWORK_AUTHENTICATION_REQUIRED = 511;\n" "\n" " protected static $reasonPhrases = [\n" - " self::CONTINUE => 'Continue',\n" - " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" - " self::PROCESSING => 'Processing',\n" - " self::OK => 'OK',\n" - " self::CREATED => 'Created',\n" - " self::ACCEPTED => 'Accepted',\n" - " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" - " self::NO_CONTENT => 'No Content',\n" - " self::RESET_CONTENT => 'Reset Content',\n" - " self::PARTIAL_CONTENT => 'Partial Content',\n" - " self::MULTI_STATUS => 'Multi-status',\n" - " self::ALREADY_REPORTED => 'Already Reported',\n" - " self::IM_USED => 'IM Used',\n" - " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" - " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" - " self::FOUND => 'Found',\n" - " self::SEE_OTHER => 'See Other',\n" - " self::NOT_MODIFIED => 'Not Modified',\n" - " self::USE_PROXY => 'Use Proxy',\n" - " self::SWITCH_PROXY => 'Switch Proxy',\n" - " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" - " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" - " self::BAD_REQUEST => 'Bad Request',\n" - " self::UNAUTHORIZED => 'Unauthorized',\n" - " self::PAYMENT_REQUIRED => 'Payment Required',\n" - " self::FORBIDDEN => 'Forbidden',\n" - " self::NOT_FOUND => 'Not Found',\n" - " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" - " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" - " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" - " self::REQUEST_TIME_OUT => 'Request Time-out',\n" - " self::CONFLICT => 'Conflict',\n" - " self::GONE => 'Gone',\n" - " self::LENGTH_REQUIRED => 'Length Required',\n" - " self::PRECONDITION_FAILED => 'Precondition Failed',\n" - " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" - " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" - " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" + " self::CONTINUE => 'Continue',\n" + " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" + " self::PROCESSING => 'Processing',\n" + " self::OK => 'OK',\n" + " self::CREATED => 'Created',\n" + " self::ACCEPTED => 'Accepted',\n" + " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" + " self::NO_CONTENT => 'No Content',\n" + " self::RESET_CONTENT => 'Reset Content',\n" + " self::PARTIAL_CONTENT => 'Partial Content',\n" + " self::MULTI_STATUS => 'Multi-status',\n" + " self::ALREADY_REPORTED => 'Already Reported',\n" + " self::IM_USED => 'IM Used',\n" + " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" + " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" + " self::FOUND => 'Found',\n" + " self::SEE_OTHER => 'See Other',\n" + " self::NOT_MODIFIED => 'Not Modified',\n" + " self::USE_PROXY => 'Use Proxy',\n" + " self::SWITCH_PROXY => 'Switch Proxy',\n" + " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" + " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" + " self::BAD_REQUEST => 'Bad Request',\n" + " self::UNAUTHORIZED => 'Unauthorized',\n" + " self::PAYMENT_REQUIRED => 'Payment Required',\n" + " self::FORBIDDEN => 'Forbidden',\n" + " self::NOT_FOUND => 'Not Found',\n" + " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" + " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" + " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" + " self::REQUEST_TIME_OUT => 'Request Time-out',\n" + " self::CONFLICT => 'Conflict',\n" + " self::GONE => 'Gone',\n" + " self::LENGTH_REQUIRED => 'Length Required',\n" + " self::PRECONDITION_FAILED => 'Precondition Failed',\n" + " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" + " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" + " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" " self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\n" - " self::EXPECTATION_FAILED => 'Expectation Failed',\n" - " self::MISDIRECTED_REQUEST => 'Misdirected Request',\n" - " self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n" - " self::LOCKED => 'Locked',\n" - " self::FAILED_DEPENDENCY => 'Failed Dependency',\n" - " self::UNORDERED_COLLECTION => 'Unordered Collection',\n" - " self::UPGRADE_REQUIRED => 'Upgrade Required',\n" - " self::PRECONDITION_REQUIRED => 'Precondition Required',\n" - " self::TOO_MANY_REQUESTS => 'Too Many Requests',\n" + " self::EXPECTATION_FAILED => 'Expectation Failed',\n" + " self::MISDIRECTED_REQUEST => 'Misdirected Request',\n" + " self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n" + " self::LOCKED => 'Locked',\n" + " self::FAILED_DEPENDENCY => 'Failed Dependency',\n" + " self::UNORDERED_COLLECTION => 'Unordered Collection',\n" + " self::UPGRADE_REQUIRED => 'Upgrade Required',\n" + " self::PRECONDITION_REQUIRED => 'Precondition Required',\n" + " self::TOO_MANY_REQUESTS => 'Too Many Requests',\n" " self::REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large',\n" - " self::UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons',\n" - " self::INTERNAL_SERVER_ERROR => 'Internal Server Error',\n" - " self::NOT_IMPLEMENTED => 'Not Implemented',\n" - " self::BAD_GATEWAY => 'Bad Gateway',\n" - " self::SERVICE_UNAVAILABLE => 'Service Unavailable',\n" - " self::GATEWAY_TIME_OUT => 'Gateway Time-out',\n" - " self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported',\n" - " self::VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',\n" - " self::INSUFFICIENT_STORAGE => 'Insufficient Storage',\n" - " self::LOOP_DETECTED => 'Loop Detected',\n" - " self::NOT_EXTENDED => 'Not Extended',\n" + " self::UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons',\n" + " self::INTERNAL_SERVER_ERROR => 'Internal Server Error',\n" + " self::NOT_IMPLEMENTED => 'Not Implemented',\n" + " self::BAD_GATEWAY => 'Bad Gateway',\n" + " self::SERVICE_UNAVAILABLE => 'Service Unavailable',\n" + " self::GATEWAY_TIME_OUT => 'Gateway Time-out',\n" + " self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported',\n" + " self::VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',\n" + " self::INSUFFICIENT_STORAGE => 'Insufficient Storage',\n" + " self::LOOP_DETECTED => 'Loop Detected',\n" + " self::NOT_EXTENDED => 'Not Extended',\n" " self::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',\n" " ];\n" "\n" @@ -3487,53 +3737,51 @@ static const char* swoole_library_source_core_curl_handler = "\n" "namespace Swoole\\Curl;\n" "\n" - "use CURLFile;\n" - "use ReflectionClass;\n" - "use Swoole;\n" "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\Http\\Client;\n" + "use Swoole\\Coroutine\\System;\n" "use Swoole\\Curl\\Exception as CurlException;\n" "use Swoole\\Http\\Status;\n" "\n" - "final class Handler\n" + "final class Handler implements \\Stringable\n" "{\n" " /**\n" - " * @var Client\n" + " * @var Client|null\n" " */\n" " private $client;\n" "\n" " private $info = [\n" - " 'url' => '',\n" - " 'content_type' => '',\n" - " 'http_code' => 0,\n" - " 'header_size' => 0,\n" - " 'request_size' => 0,\n" - " 'filetime' => -1,\n" - " 'ssl_verify_result' => 0,\n" - " 'redirect_count' => 0,\n" - " 'total_time' => 5.3E-5,\n" - " 'namelookup_time' => 0.0,\n" - " 'connect_time' => 0.0,\n" - " 'pretransfer_time' => 0.0,\n" - " 'size_upload' => 0.0,\n" - " 'size_download' => 0.0,\n" - " 'speed_download' => 0.0,\n" - " 'speed_upload' => 0.0,\n" + " 'url' => '',\n" + " 'content_type' => '',\n" + " 'http_code' => 0,\n" + " 'header_size' => 0,\n" + " 'request_size' => 0,\n" + " 'filetime' => -1,\n" + " 'ssl_verify_result' => 0,\n" + " 'redirect_count' => 0,\n" + " 'total_time' => 5.3E-5,\n" + " 'namelookup_time' => 0.0,\n" + " 'connect_time' => 0.0,\n" + " 'pretransfer_time' => 0.0,\n" + " 'size_upload' => 0.0,\n" + " 'size_download' => 0.0,\n" + " 'speed_download' => 0.0,\n" + " 'speed_upload' => 0.0,\n" " 'download_content_length' => -1.0,\n" - " 'upload_content_length' => -1.0,\n" - " 'starttransfer_time' => 0.0,\n" - " 'redirect_time' => 0.0,\n" - " 'redirect_url' => '',\n" - " 'primary_ip' => '',\n" - " 'certinfo' => [],\n" - " 'primary_port' => 0,\n" - " 'local_ip' => '',\n" - " 'local_port' => 0,\n" - " 'http_version' => 0,\n" - " 'protocol' => 0,\n" - " 'ssl_verifyresult' => 0,\n" - " 'scheme' => '',\n" - " 'private' => '',\n" + " 'upload_content_length' => -1.0,\n" + " 'starttransfer_time' => 0.0,\n" + " 'redirect_time' => 0.0,\n" + " 'redirect_url' => '',\n" + " 'primary_ip' => '',\n" + " 'certinfo' => [],\n" + " 'primary_port' => 0,\n" + " 'local_ip' => '',\n" + " 'local_port' => 0,\n" + " 'http_version' => 0,\n" + " 'protocol' => 0,\n" + " 'ssl_verifyresult' => 0,\n" + " 'scheme' => '',\n" + " 'private' => '',\n" " ];\n" "\n" " private $withHeaderOut = false;\n" @@ -3572,13 +3820,13 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private $nobody = false;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $headerFunction;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $readFunction;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $writeFunction;\n" "\n" " private $noProgress = true;\n" @@ -3617,7 +3865,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " public function __toString()\n" + " public function __toString(): string\n" " {\n" " $id = spl_object_id($this);\n" " return \"Object({$id}) of type (curl)\";\n" @@ -3652,14 +3900,14 @@ static const char* swoole_library_source_core_curl_handler = " return $this->isAvailable() ? $this->info : false;\n" " }\n" "\n" - " public function errno()\n" + " public function errno(): int\n" " {\n" - " return $this->isAvailable() ? $this->errCode : false;\n" + " return $this->isAvailable() ? $this->errCode : 0;\n" " }\n" "\n" - " public function error()\n" + " public function error(): string\n" " {\n" - " return $this->isAvailable() ? $this->errMsg : false;\n" + " return $this->isAvailable() ? $this->errMsg : '';\n" " }\n" "\n" " public function reset()\n" @@ -3667,7 +3915,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!$this->isAvailable()) {\n" " return false;\n" " }\n" - " foreach ((new ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" + " foreach ((new \\ReflectionClass(self::class))->getDefaultProperties() as $name => $value) {\n" " $this->{$name} = $value;\n" " }\n" " }\n" @@ -3680,12 +3928,12 @@ static const char* swoole_library_source_core_curl_handler = " return $this->transfer;\n" " }\n" "\n" - " public function close()\n" + " public function close(): void\n" " {\n" " if (!$this->isAvailable()) {\n" - " return false;\n" + " return;\n" " }\n" - " foreach ($this as &$property) {\n" + " foreach ($this as &$property) { // @phpstan-ignore foreach.nonIterable\n" " $property = null;\n" " }\n" " $this->closed = true;\n" @@ -3736,7 +3984,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->setError(CURLE_URL_MALFORMAT, 'No URL set!');\n" " return false;\n" " }\n" - " if (strpos($url, '://') === false && $this->unix_socket_path === '') {\n" + " if (!str_contains($url, '://') && $this->unix_socket_path === '') {\n" " $url = 'http://' . $url;\n" " }\n" " if ($setInfo) {\n" @@ -3781,7 +4029,7 @@ static const char* swoole_library_source_core_curl_handler = " $urlInfo['port'] = intval($urlInfo['port']);\n" " }\n" " $port = $urlInfo['port'];\n" - " if ($this->client) {\n" + " if (isset($this->client)) {\n" " $oldUrlInfo = $this->urlInfo;\n" " if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\n" " /* target changed */\n" @@ -3797,7 +4045,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['primary_port'] = $port;\n" " if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {\n" " $this->urlInfo['port'] = $port;\n" - " if ($this->client) {\n" + " if (isset($this->client)) {\n" " /* target changed */\n" " $this->create();\n" " }\n" @@ -3807,7 +4055,7 @@ static const char* swoole_library_source_core_curl_handler = " private function setError($code, $msg = ''): void\n" " {\n" " $this->errCode = $code;\n" - " $this->errMsg = $msg ?: curl_strerror($code);\n" + " $this->errMsg = $msg ?: curl_strerror($code);\n" " }\n" "\n" " private function hasHeader(string $headerName): bool\n" @@ -3824,7 +4072,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($value !== '') {\n" - " $this->headers[$headerName] = $value;\n" + " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" " // remove empty headers (keep same with raw cURL)\n" @@ -3833,10 +4081,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " /**\n" - " * @param mixed $value\n" - " * @throws Swoole\\Curl\\Exception\n" + " * @throws Exception\n" " */\n" - " private function setOption(int $opt, $value): bool\n" + " private function setOption(int $opt, mixed $value): bool\n" " {\n" " switch ($opt) {\n" " // case CURLOPT_STDERR:\n" @@ -3863,8 +4110,8 @@ static const char* swoole_library_source_core_curl_handler = " $this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;\n" " break;\n" " case CURLOPT_RETURNTRANSFER:\n" - " $this->returnTransfer = $value;\n" - " $this->transfer = '';\n" + " $this->returnTransfer = (bool) $value;\n" + " $this->transfer = '';\n" " break;\n" " case CURLOPT_ENCODING:\n" " if (empty($value)) {\n" @@ -3886,9 +4133,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_PROXYTYPE:\n" " if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\n" - " throw new Swoole\\Curl\\Exception(\n" - " 'swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5'\n" - " );\n" + " throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\n" " }\n" " $this->proxyType = $value;\n" " break;\n" @@ -3905,7 +4150,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->proxyPassword = $value;\n" " break;\n" " case CURLOPT_PROXYUSERPWD:\n" - " $usernamePassword = explode(':', $value);\n" + " $usernamePassword = explode(':', $value);\n" " $this->proxyUsername = urldecode($usernamePassword[0]);\n" " $this->proxyPassword = urldecode($usernamePassword[1] ?? null);\n" " break;\n" @@ -3932,9 +4177,9 @@ static const char* swoole_library_source_core_curl_handler = " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" - " $host = $tmpResolve[0] ?? '';\n" - " $port = $tmpResolve[1] ?? 0;\n" - " $ip = $tmpResolve[2] ?? '';\n" + " $host = $tmpResolve[0];\n" + " $port = $tmpResolve[1] ?? 0;\n" + " $ip = $tmpResolve[2] ?? '';\n" " if ($flag === '-') {\n" " unset($this->resolve[$host][$port]);\n" " } else {\n" @@ -3945,9 +4190,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_IPRESOLVE:\n" " if ($value !== CURL_IPRESOLVE_WHATEVER and $value !== CURL_IPRESOLVE_V4) {\n" - " throw new Swoole\\Curl\\Exception(\n" - " 'swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, 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" " break;\n" " case CURLOPT_TCP_NODELAY:\n" @@ -3956,18 +4199,14 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_PRIVATE:\n" " $this->info['private'] = $value;\n" " break;\n" - " /*\n" - " * Ignore options\n" - " */\n" + " /*\n" + " * Ignore options\n" + " */\n" " case CURLOPT_VERBOSE:\n" " // trigger_error('swoole_curl_setopt(): CURLOPT_VERBOSE is not supported', E_USER_WARNING);\n" " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" - " /*\n" - " * From PHP 5.1.3, this option has no effect: the raw output will always be returned when CURLOPT_RETURNTRANSFER is used.\n" - " */\n" - " case CURLOPT_BINARYTRANSFER: /* TODO */\n" " case CURLOPT_DNS_USE_GLOBAL_CACHE:\n" " case CURLOPT_DNS_CACHE_TIMEOUT:\n" " case CURLOPT_STDERR:\n" @@ -3979,10 +4218,11 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_CERTINFO:\n" " case CURLOPT_HEADEROPT:\n" " case CURLOPT_PROXYHEADER:\n" + " case CURLOPT_HTTPPROXYTUNNEL:\n" " break;\n" - " /*\n" - " * SSL\n" - " */\n" + " /*\n" + " * SSL\n" + " */\n" " case CURLOPT_SSL_VERIFYHOST:\n" " break;\n" " case CURLOPT_SSL_VERIFYPEER:\n" @@ -4005,9 +4245,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_SSLKEYPASSWD:\n" " $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\n" " break;\n" - " /*\n" - " * Http POST\n" - " */\n" + " /*\n" + " * Http POST\n" + " */\n" " case CURLOPT_POST:\n" " $this->method = 'POST';\n" " break;\n" @@ -4017,26 +4257,26 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'POST';\n" " }\n" " break;\n" - " /*\n" - " * Upload\n" - " */\n" + " /*\n" + " * Upload\n" + " */\n" " case CURLOPT_SAFE_UPLOAD:\n" " if (!$value) {\n" " trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);\n" " return false;\n" " }\n" " break;\n" - " /*\n" - " * Http Header\n" - " */\n" + " /*\n" + " * Http Header\n" + " */\n" " case CURLOPT_HTTPHEADER:\n" " if (!is_array($value) and !is_iterable($value)) {\n" " trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);\n" " return false;\n" " }\n" " foreach ($value as $header) {\n" - " $header = explode(':', $header, 2);\n" - " $headerName = $header[0];\n" + " $header = explode(':', $header, 2);\n" + " $headerName = $header[0];\n" " $headerValue = trim($header[1] ?? '');\n" " $this->setHeader($headerName, $headerValue);\n" " }\n" @@ -4075,9 +4315,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_FAILONERROR:\n" " $this->failOnError = $value;\n" " break;\n" - " /*\n" - " * Http Cookie\n" - " */\n" + " /*\n" + " * Http Cookie\n" + " */\n" " case CURLOPT_COOKIE:\n" " $this->setHeader('Cookie', $value);\n" " break;\n" @@ -4156,7 +4396,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'GET';\n" " break;\n" " default:\n" - " throw new Swoole\\Curl\\Exception(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" + " throw new CurlException(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" " }\n" " return true;\n" " }\n" @@ -4164,8 +4404,8 @@ static const char* swoole_library_source_core_curl_handler = " private function execute()\n" " {\n" " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" - " $this->info['redirect_url'] = '';\n" - " $timeBegin = microtime(true);\n" + " $this->info['redirect_url'] = '';\n" + " $timeBegin = microtime(true);\n" " /*\n" " * Socket\n" " */\n" @@ -4173,7 +4413,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\n" " return false;\n" " }\n" - " if (!$this->client) {\n" + " if (!isset($this->client)) {\n" " $this->create();\n" " }\n" " while (true) {\n" @@ -4182,12 +4422,12 @@ static const char* swoole_library_source_core_curl_handler = " * Http Proxy\n" " */\n" " if ($this->proxy) {\n" - " $parse = parse_url($this->proxy);\n" - " $proxy = $parse['host'] ?? $parse['path'];\n" - " $proxyPort = $parse['port'] ?? $this->proxyPort;\n" + " $parse = parse_url($this->proxy);\n" + " $proxy = $parse['host'] ?? $parse['path'];\n" + " $proxyPort = $parse['port'] ?? $this->proxyPort;\n" " $proxyUsername = $parse['user'] ?? $this->proxyUsername;\n" " $proxyPassword = $parse['pass'] ?? $this->proxyPassword;\n" - " $proxyType = $parse['scheme'] ?? $this->proxyType;\n" + " $proxyType = $parse['scheme'] ?? $this->proxyType;\n" " if (is_string($proxyType)) {\n" " if ($proxyType === 'socks5') {\n" " $proxyType = CURLPROXY_SOCKS5;\n" @@ -4197,33 +4437,28 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if (!filter_var($proxy, FILTER_VALIDATE_IP)) {\n" - " $ip = Swoole\\Coroutine::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);\n" + " $ip = System::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);\n" " if (!$ip) {\n" " $this->setError(CURLE_COULDNT_RESOLVE_PROXY, 'Could not resolve proxy: ' . $proxy);\n" " return false;\n" " }\n" " $this->proxy = $proxy = $ip;\n" " }\n" - " switch ($proxyType) {\n" - " case CURLPROXY_HTTP:\n" - " $proxyOptions = [\n" - " 'http_proxy_host' => $proxy,\n" - " 'http_proxy_port' => $proxyPort,\n" - " 'http_proxy_username' => $proxyUsername,\n" - " 'http_proxy_password' => $proxyPassword,\n" - " ];\n" - " break;\n" - " case CURLPROXY_SOCKS5:\n" - " $proxyOptions = [\n" - " 'socks5_host' => $proxy,\n" - " 'socks5_port' => $proxyPort,\n" - " 'socks5_username' => $proxyUsername,\n" - " 'socks5_password' => $proxyPassword,\n" - " ];\n" - " break;\n" - " default:\n" - " throw new CurlException(\"Unexpected proxy type [{$proxyType}]\");\n" - " }\n" + " $proxyOptions = match ($proxyType) {\n" + " CURLPROXY_HTTP => [\n" + " 'http_proxy_host' => $proxy,\n" + " 'http_proxy_port' => $proxyPort,\n" + " 'http_proxy_username' => $proxyUsername,\n" + " 'http_proxy_password' => $proxyPassword,\n" + " ],\n" + " CURLPROXY_SOCKS5 => [\n" + " 'socks5_host' => $proxy,\n" + " 'socks5_port' => $proxyPort,\n" + " 'socks5_username' => $proxyUsername,\n" + " 'socks5_password' => $proxyPassword,\n" + " ],\n" + " default => throw new CurlException(\"Unexpected proxy type [{$proxyType}]\"),\n" + " };\n" " }\n" " /*\n" " * Client Options\n" @@ -4257,7 +4492,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $client->setData($data);\n" " // Notice: although we reset it, raw cURL never do this\n" - " $this->infile = null;\n" + " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" " // POST data\n" @@ -4268,14 +4503,14 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " } elseif (is_array($this->postData)) {\n" " foreach ($this->postData as $k => $v) {\n" - " if ($v instanceof CURLFile) {\n" + " if ($v instanceof \\CURLFile) {\n" " $client->addFile($v->getFilename(), $k, $v->getMimeType() ?: 'application/octet-stream', $v->getPostFilename());\n" " unset($this->postData[$k]);\n" " }\n" " }\n" " }\n" + " $client->setData($this->postData);\n" " }\n" - " $client->setData($this->postData);\n" " }\n" " /*\n" " * Headers\n" @@ -4300,11 +4535,11 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {\n" " $redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);\n" - " $redirectUrl = static::unparseUrl($redirectParsedUrl);\n" + " $redirectUrl = self::unparseUrl($redirectParsedUrl);\n" " if ($this->followLocation and ($this->maxRedirects === null or $this->info['redirect_count'] < $this->maxRedirects)) {\n" " if ($this->info['redirect_count'] === 0) {\n" " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" - " $redirectBeginTime = microtime(true);\n" + " $redirectBeginTime = microtime(true);\n" " }\n" " // force GET\n" " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" @@ -4327,10 +4562,10 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " }\n" " }\n" - " $this->info['total_time'] = microtime(true) - $timeBegin;\n" - " $this->info['http_code'] = $client->statusCode;\n" - " $this->info['content_type'] = $client->headers['content-type'] ?? '';\n" - " $this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);\n" + " $this->info['total_time'] = microtime(true) - $timeBegin;\n" + " $this->info['http_code'] = $client->statusCode;\n" + " $this->info['content_type'] = $client->headers['content-type'] ?? '';\n" + " $this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);\n" " $this->info['speed_download'] = 1 / $this->info['total_time'] * $this->info['size_download'];\n" " if (isset($redirectBeginTime)) {\n" " $this->info['redirect_time'] = microtime(true) - $redirectBeginTime;\n" @@ -4341,7 +4576,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->unix_socket_path) {\n" - " $this->info['primary_ip'] = $this->unix_socket_path;\n" + " $this->info['primary_ip'] = $this->unix_socket_path;\n" " $this->info['primary_port'] = $this->urlInfo['port'];\n" " }\n" "\n" @@ -4383,7 +4618,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->withHeaderOut) {\n" - " $headerOutContent = $client->getHeaderOut();\n" + " $headerOutContent = $client->getHeaderOut();\n" " $this->info['request_header'] = $headerOutContent ? $headerOutContent . \"\\r\\n\\r\\n\" : '';\n" " }\n" " if ($this->withFileTime) {\n" @@ -4394,7 +4629,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " if ($this->cookieJar && $this->cookieJar !== '') {\n" + " if (!empty($this->cookieJar)) {\n" " if ($this->cookieJar === '-') {\n" " foreach ((array) $client->set_cookie_headers as $cookie) {\n" " echo $cookie . PHP_EOL;\n" @@ -4409,7 +4644,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->writeFunction) {\n" - " if (!is_callable($this->writeFunction)) {\n" + " if (!is_callable($this->writeFunction)) { // @phpstan-ignore booleanNot.alwaysFalse\n" " trigger_error('curl_exec(): Could not call the CURLOPT_WRITEFUNCTION', E_USER_WARNING);\n" " $this->setError(CURLE_WRITE_ERROR, 'Failure writing output to destination');\n" " return false;\n" @@ -4433,14 +4668,14 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private static function unparseUrl(array $parsedUrl): string\n" " {\n" - " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" - " $host = $parsedUrl['host'] ?? '';\n" - " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" - " $user = $parsedUrl['user'] ?? '';\n" - " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" - " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" - " $path = $parsedUrl['path'] ?? '';\n" - " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" + " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" + " $host = $parsedUrl['host'] ?? '';\n" + " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" + " $user = $parsedUrl['user'] ?? '';\n" + " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" + " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" + " $path = $parsedUrl['path'] ?? '';\n" + " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" " $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\n" " return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\n" " }\n" @@ -4454,7 +4689,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!isset($location[0])) {\n" " return [];\n" " }\n" - " $redirectUri = $this->urlInfo;\n" + " $redirectUri = $this->urlInfo;\n" " $redirectUri['query'] = '';\n" " if ($location[0] === '/') {\n" " $redirectUri['path'] = $location;\n" @@ -4463,7 +4698,7 @@ static const char* swoole_library_source_core_curl_handler = " if ($path === '.') {\n" " $path = '/';\n" " }\n" - " if (isset($location[1]) and substr($location, 0, 2) === './') {\n" + " if (isset($location[1]) and str_starts_with($location, './')) {\n" " $location = substr($location, 2);\n" " }\n" " $redirectUri['path'] = $path . $location;\n" @@ -4595,63 +4830,47 @@ static const char* swoole_library_source_core_fast_cgi_record = "/**\n" " * FastCGI record.\n" " */\n" - "class Record\n" + "class Record implements \\Stringable\n" "{\n" " /**\n" " * Identifies the FastCGI protocol version.\n" - " *\n" - " * @var int\n" " */\n" - " protected $version = FastCGI::VERSION_1;\n" + " protected int $version = FastCGI::VERSION_1;\n" "\n" " /**\n" " * Identifies the FastCGI record type, i.e. the general function that the record performs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type = FastCGI::UNKNOWN_TYPE;\n" + " protected int $type = FastCGI::UNKNOWN_TYPE;\n" "\n" " /**\n" " * Identifies the FastCGI request to which the record belongs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" + " protected int $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" "\n" " /**\n" " * Reserved byte for future proposes\n" - " *\n" - " * @var int\n" " */\n" - " protected $reserved = 0;\n" + " protected int $reserved = 0;\n" "\n" " /**\n" " * The number of bytes in the contentData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $contentLength = 0;\n" + " private int $contentLength = 0;\n" "\n" " /**\n" " * The number of bytes in the paddingData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $paddingLength = 0;\n" + " private int $paddingLength = 0;\n" "\n" " /**\n" " * Binary data, between 0 and 65535 bytes of data, interpreted according to the record type.\n" - " *\n" - " * @var string\n" " */\n" - " private $contentData = '';\n" + " private string $contentData = '';\n" "\n" " /**\n" " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" - " *\n" - " * @var string\n" " */\n" - " private $paddingData = '';\n" + " private string $paddingData = '';\n" "\n" " /**\n" " * Returns the binary message representation of record\n" @@ -4676,26 +4895,29 @@ static const char* swoole_library_source_core_fast_cgi_record = "\n" " /**\n" " * Unpacks the message from the binary data buffer\n" - " *\n" - " * @param string $data Binary buffer with raw data\n" - " *\n" - " * @return static\n" " */\n" - " final public static function unpack(string $data): self\n" + " final public static function unpack(string $binaryData): static\n" " {\n" - " $self = new static();\n" + " /** @var static $self */\n" + " $self = (new \\ReflectionClass(static::class))->newInstanceWithoutConstructor();\n" + "\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int, reserved: int} */\n" + " $packet = unpack(FastCGI::HEADER_FORMAT, $binaryData);\n" + " if ($packet === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->version,\n" " $self->type,\n" " $self->requestId,\n" " $self->contentLength,\n" " $self->paddingLength,\n" - " $self->reserved\n" - " ] = array_values(unpack(FastCGI::HEADER_FORMAT, $data));\n" + " $self->reserved,\n" + " ] = array_values($packet);\n" "\n" - " $payload = substr($data, FastCGI::HEADER_LEN);\n" + " $payload = substr($binaryData, FastCGI::HEADER_LEN);\n" " self::unpackPayload($self, $payload);\n" - " if (get_called_class() !== __CLASS__ && $self->contentLength > 0) {\n" + " if (static::class !== self::class && $self->contentLength > 0) {\n" " static::unpackPayload($self, $payload);\n" " }\n" "\n" @@ -4712,11 +4934,11 @@ static const char* swoole_library_source_core_fast_cgi_record = " $this->contentLength = strlen($data);\n" " if ($this->contentLength > FastCGI::MAX_CONTENT_LENGTH) {\n" " $this->contentLength = FastCGI::MAX_CONTENT_LENGTH;\n" - " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" + " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" " } else {\n" " $this->contentData = $data;\n" " }\n" - " $extraLength = $this->contentLength % 8;\n" + " $extraLength = $this->contentLength % 8;\n" " $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\n" " return $this;\n" " }\n" @@ -4787,18 +5009,18 @@ static const char* swoole_library_source_core_fast_cgi_record = " * Method to unpack the payload for the record.\n" " *\n" " * NB: Default implementation will be always called\n" - " *\n" - " * @param static $self Instance of current frame\n" - " * @param string $data Binary data\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(self $self, string $binaryData): void\n" " {\n" + " /** @phpstan-var false|array{contentData: string, paddingData: string} */\n" + " $payload = unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->contentData,\n" - " $self->paddingData\n" - " ] = array_values(\n" - " unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $data)\n" - " );\n" + " $self->paddingData,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /**\n" @@ -4835,22 +5057,27 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " /**\n" " * List of params\n" " *\n" - " * @var array\n" + " * @var string[]\n" + " * @phpstan-var array\n" " */\n" - " protected $values = [];\n" + " protected array $values = [];\n" "\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" - " $this->type = FastCGI::PARAMS;\n" + " $this->type = FastCGI::PARAMS;\n" " $this->values = $values;\n" " $this->setContentData($this->packPayload());\n" " }\n" "\n" " /**\n" " * Returns an associative list of parameters\n" + " *\n" + " * @phpstan-return array\n" " */\n" " public function getValues(): array\n" " {\n" @@ -4861,55 +5088,77 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" " $currentOffset = 0;\n" " do {\n" - " [$nameLengthHigh] = array_values(unpack('CnameLengthHigh', $data));\n" - " $isLongName = ($nameLengthHigh >> 7 == 1);\n" - " $valueOffset = $isLongName ? 4 : 1;\n" - "\n" - " [$valueLengthHigh] = array_values(unpack('CvalueLengthHigh', substr($data, $valueOffset)));\n" - " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" - " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" + " /** @phpstan-var false|array{nameLengthHigh: int} */\n" + " $payload = unpack('CnameLengthHigh', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLengthHigh] = array_values($payload);\n" + " $isLongName = ($nameLengthHigh >> 7 == 1);\n" + " $valueOffset = $isLongName ? 4 : 1;\n" + "\n" + " /** @phpstan-var false|array{valueLengthHigh: int} */\n" + " $payload = unpack('CvalueLengthHigh', substr($binaryData, $valueOffset));\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$valueLengthHigh] = array_values($payload);\n" + " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" + " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" "\n" " $formatParts = [\n" " $isLongName ? 'NnameLength' : 'CnameLength',\n" " $isLongValue ? 'NvalueLength' : 'CvalueLength',\n" " ];\n" - " $format = join('/', $formatParts);\n" - " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" + " $format = join('/', $formatParts);\n" + "\n" + " /** @phpstan-var false|array{nameLength: int, valueLength: int} */\n" + " $payload = unpack($format, $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLength, $valueLength] = array_values($payload);\n" "\n" " // Clear top bit for long record\n" " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\n" "\n" - " [$nameData, $valueData] = array_values(\n" - " unpack(\n" - " \"a{$nameLength}nameData/a{$valueLength}valueData\",\n" - " substr($data, $dataOffset)\n" - " )\n" + " /** @phpstan-var false|array{nameData: string, valueData: string} */\n" + " $payload = unpack(\n" + " \"a{$nameLength}nameData/a{$valueLength}valueData\",\n" + " substr($binaryData, $dataOffset)\n" " );\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameData, $valueData] = array_values($payload);\n" "\n" " $self->values[$nameData] = $valueData;\n" "\n" " $keyValueLength = $dataOffset + $nameLength + $valueLength;\n" - " $data = substr($data, $keyValueLength);\n" + " $binaryData = substr($binaryData, $keyValueLength);\n" " $currentOffset += $keyValueLength;\n" " } while ($currentOffset < $self->getContentLength());\n" " }\n" "\n" - " /** {@inheritdoc} */\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" " protected function packPayload(): string\n" " {\n" " $payload = '';\n" " foreach ($this->values as $nameData => $valueData) {\n" - " if ($valueData === null) {\n" + " if ($valueData === null) { // @phpstan-ignore identical.alwaysFalse\n" " continue;\n" " }\n" - " $nameLength = strlen($nameData);\n" + " $nameLength = strlen($nameData);\n" " $valueLength = strlen((string) $valueData);\n" - " $isLongName = $nameLength > 127;\n" + " $isLongName = $nameLength > 127;\n" " $isLongValue = $valueLength > 127;\n" " $formatParts = [\n" " $isLongName ? 'N' : 'C',\n" @@ -4917,6 +5166,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " \"a{$nameLength}\",\n" " \"a{$valueLength}\",\n" " ];\n" + "\n" " $format = join('', $formatParts);\n" "\n" " $payload .= pack(\n" @@ -4954,7 +5204,7 @@ static const char* swoole_library_source_core_fast_cgi_record_abort_request = " */\n" "class AbortRequest extends Record\n" "{\n" - " public function __construct(int $requestId = 0)\n" + " public function __construct(int $requestId)\n" " {\n" " $this->type = FastCGI::ABORT_REQUEST;\n" " $this->setRequestId($requestId);\n" @@ -4989,10 +5239,8 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * FCGI_RESPONDER\n" " * FCGI_AUTHORIZER\n" " * FCGI_FILTER\n" - " *\n" - " * @var int\n" " */\n" - " protected $role = FastCGI::UNKNOWN_ROLE;\n" + " protected int $role = FastCGI::UNKNOWN_ROLE;\n" "\n" " /**\n" " * The flags component contains a bit that controls connection shutdown.\n" @@ -5001,23 +5249,19 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * If zero, the application closes the connection after responding to this request.\n" " * If not zero, the application does not close the connection after responding to this request;\n" " * the Web server retains responsibility for the connection.\n" - " *\n" - " * @var int\n" " */\n" - " protected $flags;\n" + " protected int $flags;\n" "\n" " /**\n" " * Reserved data, 5 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" " public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::BEGIN_REQUEST;\n" - " $this->role = $role;\n" - " $this->flags = $flags;\n" + " $this->type = FastCGI::BEGIN_REQUEST;\n" + " $this->role = $role;\n" + " $this->flags = $flags;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5055,13 +5299,20 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{role: int, flags: int, reserved: string} */\n" + " $payload = unpack('nrole/Cflags/a5reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->role,\n" " $self->flags,\n" - " $self->reserved1\n" - " ] = array_values(unpack('nrole/Cflags/a5reserved', $data));\n" + " $self->reserved1,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -5100,7 +5351,7 @@ static const char* swoole_library_source_core_fast_cgi_record_data = " */\n" "class Data extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::DATA;\n" " $this->setContentData($contentData);\n" @@ -5132,10 +5383,8 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = "{\n" " /**\n" " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" - " *\n" - " * @var int\n" " */\n" - " protected $appStatus = 0;\n" + " protected int $appStatus = 0;\n" "\n" " /**\n" " * The protocolStatus component is a protocol-level status code.\n" @@ -5149,27 +5398,20 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * This happens when the application runs out of some resource, e.g. database connections.\n" " * FCGI_UNKNOWN_ROLE: rejecting a new request.\n" " * This happens when the Web server has specified a role that is unknown to the application.\n" - " *\n" - " * @var int\n" " */\n" - " protected $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" + " protected int $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" "\n" " /**\n" " * Reserved data, 3 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(\n" - " int $protocolStatus = FastCGI::REQUEST_COMPLETE,\n" - " int $appStatus = 0,\n" - " string $reserved = ''\n" - " ) {\n" - " $this->type = FastCGI::END_REQUEST;\n" + " public function __construct(int $protocolStatus = FastCGI::REQUEST_COMPLETE, int $appStatus = 0, string $reserved = '')\n" + " {\n" + " $this->type = FastCGI::END_REQUEST;\n" " $this->protocolStatus = $protocolStatus;\n" - " $this->appStatus = $appStatus;\n" - " $this->reserved1 = $reserved;\n" + " $this->appStatus = $appStatus;\n" + " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" "\n" @@ -5205,13 +5447,20 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{appStatus: int, protocolStatus: int, reserved: string} */\n" + " $payload = unpack('NappStatus/CprotocolStatus/a3reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->appStatus,\n" " $self->protocolStatus,\n" - " $self->reserved1\n" - " ] = array_values(unpack('NappStatus/CprotocolStatus/a3reserved', $data));\n" + " $self->reserved1,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -5268,8 +5517,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values = " * Constructs a request\n" " *\n" " * @param array $keys List of keys to receive\n" + " *\n" + " * @phpstan-param list $keys\n" " */\n" - " public function __construct(array $keys = [])\n" + " public function __construct(array $keys)\n" " {\n" " parent::__construct(array_fill_keys($keys, ''));\n" " $this->type = FastCGI::GET_VALUES;\n" @@ -5316,8 +5567,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values_result "{\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" " parent::__construct($values);\n" " $this->type = FastCGI::GET_VALUES_RESULT;\n" @@ -5348,7 +5601,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdin = " */\n" "class Stdin extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDIN;\n" " $this->setContentData($contentData);\n" @@ -5379,7 +5632,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdout = " */\n" "class Stdout extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDOUT;\n" " $this->setContentData($contentData);\n" @@ -5410,7 +5663,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stderr = " */\n" "class Stderr extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDERR;\n" " $this->setContentData($contentData);\n" @@ -5446,22 +5699,18 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "{\n" " /**\n" " * Type of the unrecognized management record.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type1;\n" + " protected int $type1;\n" "\n" " /**\n" " * Reserved data, 7 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(int $type = 0, string $reserved = '')\n" + " public function __construct(int $type, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::UNKNOWN_TYPE;\n" - " $this->type1 = $type;\n" + " $this->type = FastCGI::UNKNOWN_TYPE;\n" + " $this->type1 = $type;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5478,12 +5727,21 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " public static function unpackPayload($self, string $data): void\n" + " public static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " [$self->type1, $self->reserved1] = array_values(unpack('Ctype/a7reserved', $data));\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{type: int, reserved: string} */\n" + " $payload = unpack('Ctype/a7reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$self->type1, $self->reserved1] = array_values($payload);\n" " }\n" "\n" - " /** {@inheritdoc} */\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -5508,9 +5766,18 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use DomainException;\n" - "use RuntimeException;\n" "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record\\AbortRequest;\n" + "use Swoole\\FastCGI\\Record\\BeginRequest;\n" + "use Swoole\\FastCGI\\Record\\Data;\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\GetValues;\n" + "use Swoole\\FastCGI\\Record\\GetValuesResult;\n" + "use Swoole\\FastCGI\\Record\\Params;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdin;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" + "use Swoole\\FastCGI\\Record\\UnknownType;\n" "\n" "/**\n" " * Utility class to simplify parsing of FastCGI protocol data.\n" @@ -5520,35 +5787,37 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Mapping of constants to the classes\n" " *\n" - " * @var array\n" - " */\n" - " protected static $classMapping = [\n" - " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" - " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" - " FastCGI::END_REQUEST => FastCGI\\Record\\EndRequest::class,\n" - " FastCGI::PARAMS => FastCGI\\Record\\Params::class,\n" - " FastCGI::STDIN => FastCGI\\Record\\Stdin::class,\n" - " FastCGI::STDOUT => FastCGI\\Record\\Stdout::class,\n" - " FastCGI::STDERR => FastCGI\\Record\\Stderr::class,\n" - " FastCGI::DATA => FastCGI\\Record\\Data::class,\n" - " FastCGI::GET_VALUES => FastCGI\\Record\\GetValues::class,\n" - " FastCGI::GET_VALUES_RESULT => FastCGI\\Record\\GetValuesResult::class,\n" - " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" + " * @phpstan-var array\n" + " */\n" + " protected static array $classMapping = [\n" + " FastCGI::BEGIN_REQUEST => BeginRequest::class,\n" + " FastCGI::ABORT_REQUEST => AbortRequest::class,\n" + " FastCGI::END_REQUEST => EndRequest::class,\n" + " FastCGI::PARAMS => Params::class,\n" + " FastCGI::STDIN => Stdin::class,\n" + " FastCGI::STDOUT => Stdout::class,\n" + " FastCGI::STDERR => Stderr::class,\n" + " FastCGI::DATA => Data::class,\n" + " FastCGI::GET_VALUES => GetValues::class,\n" + " FastCGI::GET_VALUES_RESULT => GetValuesResult::class,\n" + " FastCGI::UNKNOWN_TYPE => UnknownType::class,\n" " ];\n" "\n" " /**\n" " * Checks if the buffer contains a valid frame to parse\n" - " *\n" - " * @param string $buffer Binary buffer\n" " */\n" - " public static function hasFrame(string $buffer): bool\n" + " public static function hasFrame(string $binaryBuffer): bool\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" " return false;\n" " }\n" "\n" - " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($fastInfo === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " if ($bufferLength < FastCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) {\n" " return false;\n" " }\n" @@ -5559,28 +5828,30 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Parses a frame from the binary buffer\n" " *\n" - " * @param string $buffer Binary buffer\n" - " *\n" " * @return Record One of the corresponding FastCGI record\n" " */\n" - " public static function parseFrame(string &$buffer): Record\n" + " public static function parseFrame(string &$binaryBuffer): Record\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" - " throw new RuntimeException('Not enough data in the buffer to parse');\n" + " throw new \\RuntimeException('Not enough data in the buffer to parse');\n" + " }\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($recordHeader === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" " }\n" - " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" " $recordType = $recordHeader['type'];\n" " if (!isset(self::$classMapping[$recordType])) {\n" - " throw new DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" + " throw new \\DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" " /** @var Record $className */\n" " $className = self::$classMapping[$recordType];\n" - " $record = $className::unpack($buffer);\n" + " $record = $className::unpack($binaryBuffer);\n" "\n" - " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" - " $buffer = substr($buffer, $offset);\n" + " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" + " $binaryBuffer = substr($binaryBuffer, $offset);\n" "\n" " return $record;\n" " }\n" @@ -5602,27 +5873,24 @@ static const char* swoole_library_source_core_fast_cgi_message = "\n" "class Message\n" "{\n" - " /** @var array */\n" - " protected $params = [];\n" + " protected array $params = [];\n" "\n" - " /** @var string */\n" - " protected $body = '';\n" + " protected string $body = '';\n" "\n" - " /** @var string */\n" - " protected $error = '';\n" + " protected string $error = '';\n" "\n" " public function getParam(string $name): ?string\n" " {\n" " return $this->params[$name] ?? null;\n" " }\n" "\n" - " public function withParam(string $name, string $value): self\n" + " public function withParam(string $name, string $value): static\n" " {\n" " $this->params[$name] = $value;\n" " return $this;\n" " }\n" "\n" - " public function withoutParam(string $name): self\n" + " public function withoutParam(string $name): static\n" " {\n" " unset($this->params[$name]);\n" " return $this;\n" @@ -5633,13 +5901,13 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->params;\n" " }\n" "\n" - " public function withParams(array $params): self\n" + " public function withParams(array $params): static\n" " {\n" " $this->params = $params;\n" " return $this;\n" " }\n" "\n" - " public function withAddedParams(array $params): self\n" + " public function withAddedParams(array $params): static\n" " {\n" " $this->params = $params + $this->params;\n" " return $this;\n" @@ -5650,7 +5918,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->body;\n" " }\n" "\n" - " public function withBody($body): self\n" + " public function withBody(string|\\Stringable $body): self\n" " {\n" " $this->body = (string) $body;\n" " return $this;\n" @@ -5661,7 +5929,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->error;\n" " }\n" "\n" - " public function withError(string $error): self\n" + " public function withError(string $error): static\n" " {\n" " $this->error = $error;\n" " return $this;\n" @@ -5687,18 +5955,18 @@ static const char* swoole_library_source_core_fast_cgi_request = "use Swoole\\FastCGI\\Record\\Params;\n" "use Swoole\\FastCGI\\Record\\Stdin;\n" "\n" - "class Request extends Message\n" + "class Request extends Message implements \\Stringable\n" "{\n" - " protected $keepConn = false;\n" + " protected bool $keepConn = false;\n" "\n" " public function __toString(): string\n" " {\n" - " $body = $this->getBody();\n" - " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, ($this->keepConn ? FastCGI::KEEP_CONN : 0));\n" - " $paramsFrame = new Params($this->getParams());\n" - " $paramsEofFrame = new Params();\n" + " $body = $this->getBody();\n" + " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\n" + " $paramsFrame = new Params($this->getParams());\n" + " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" " } else {\n" " $stdinList = [];\n" " while (true) {\n" @@ -5709,9 +5977,9 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $body = substr($body, $stdinLength);\n" " }\n" - " $stdinList[] = new Stdin();\n" - " $stdin = implode($stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" + " $stdinList[] = new Stdin('');\n" + " $stdin = implode('', $stdinList);\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" " }\n" " return $message;\n" " }\n" @@ -5742,20 +6010,22 @@ static const char* swoole_library_source_core_fast_cgi_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\Record\\EndRequest;\n" "use Swoole\\FastCGI\\Record\\Stderr;\n" "use Swoole\\FastCGI\\Record\\Stdout;\n" "\n" "class Response extends Message\n" "{\n" - " public function __construct(array $records = [])\n" + " /**\n" + " * @param array $records\n" + " */\n" + " public function __construct(array $records)\n" " {\n" " if (!static::verify($records)) {\n" - " throw new InvalidArgumentException('Bad records');\n" + " throw new \\InvalidArgumentException('Bad records');\n" " }\n" - " $body = '';\n" - " $error = '';\n" + "\n" + " $body = $error = '';\n" " foreach ($records as $record) {\n" " if ($record instanceof Stdout) {\n" " if ($record->getContentLength() > 0) {\n" @@ -5770,9 +6040,12 @@ static const char* swoole_library_source_core_fast_cgi_response = " $this->withBody($body)->withError($error);\n" " }\n" "\n" - " public static function verify(array $records): bool\n" + " /**\n" + " * @param array $records\n" + " */\n" + " protected static function verify(array $records): bool\n" " {\n" - " return !empty($records) && $records[count($records) - 1] instanceof EndRequest;\n" + " return !empty($records) && $records[array_key_last($records)] instanceof EndRequest;\n" " }\n" "}\n"; @@ -5790,30 +6063,28 @@ static const char* swoole_library_source_core_fast_cgi_http_request = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" - "\n" "class HttpRequest extends Request\n" "{\n" - " protected $params = [\n" - " 'REQUEST_SCHEME' => 'http',\n" - " 'REQUEST_METHOD' => 'GET',\n" - " 'DOCUMENT_ROOT' => '',\n" - " 'SCRIPT_FILENAME' => '',\n" - " 'SCRIPT_NAME' => '',\n" - " 'DOCUMENT_URI' => '/',\n" - " 'REQUEST_URI' => '/',\n" - " 'QUERY_STRING' => '',\n" - " 'CONTENT_TYPE' => 'text/plain',\n" - " 'CONTENT_LENGTH' => '0',\n" + " protected array $params = [\n" + " 'REQUEST_SCHEME' => 'http',\n" + " 'REQUEST_METHOD' => 'GET',\n" + " 'DOCUMENT_ROOT' => '',\n" + " 'SCRIPT_FILENAME' => '',\n" + " 'SCRIPT_NAME' => '',\n" + " 'DOCUMENT_URI' => '/',\n" + " 'REQUEST_URI' => '/',\n" + " 'QUERY_STRING' => '',\n" + " 'CONTENT_TYPE' => 'text/plain',\n" + " 'CONTENT_LENGTH' => '0',\n" " 'GATEWAY_INTERFACE' => 'CGI/1.1',\n" - " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" - " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" - " 'REMOTE_ADDR' => 'unknown',\n" - " 'REMOTE_PORT' => '0',\n" - " 'SERVER_ADDR' => 'unknown',\n" - " 'SERVER_PORT' => '0',\n" - " 'SERVER_NAME' => 'Swoole',\n" - " 'REDIRECT_STATUS' => '200',\n" + " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" + " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" + " 'REMOTE_ADDR' => 'unknown',\n" + " 'REMOTE_PORT' => '0',\n" + " 'SERVER_ADDR' => 'unknown',\n" + " 'SERVER_PORT' => '0',\n" + " 'SERVER_NAME' => 'Swoole',\n" + " 'REDIRECT_STATUS' => '200',\n" " ];\n" "\n" " public function getScheme(): ?string\n" @@ -5901,7 +6172,8 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " $info = parse_url($uri);\n" " return $this->withRequestUri($uri)\n" " ->withDocumentUri($info['path'] ?? '')\n" - " ->withQueryString($info['query'] ?? '');\n" + " ->withQueryString($info['query'] ?? '')\n" + " ;\n" " }\n" "\n" " public function getDocumentUri(): ?string\n" @@ -6027,7 +6299,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " public function withProtocolVersion(string $protocolVersion): self\n" " {\n" " if (!is_numeric($protocolVersion)) {\n" - " throw new InvalidArgumentException('Protocol version must be numeric');\n" + " throw new \\InvalidArgumentException('Protocol version must be numeric');\n" " }\n" " $this->params['SERVER_PROTOCOL'] = \"HTTP/{$protocolVersion}\";\n" " return $this;\n" @@ -6165,7 +6437,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " {\n" " $headers = [];\n" " foreach ($this->params as $name => $value) {\n" - " if (strpos($name, 'HTTP_') === 0) {\n" + " if (str_starts_with($name, 'HTTP_')) {\n" " $headers[static::convertParamNameToHeaderName($name)] = $value;\n" " }\n" " }\n" @@ -6180,14 +6452,14 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " return $this;\n" " }\n" "\n" - " /** @return $this */\n" - " public function withBody($body): Message\n" + " public function withBody(array|string|\\Stringable $body): self\n" " {\n" " if (is_array($body)) {\n" " $body = http_build_query($body);\n" " $this->withContentType('application/x-www-form-urlencoded');\n" " }\n" " parent::withBody($body);\n" + "\n" " return $this->withContentLength(strlen($body));\n" " }\n" "\n" @@ -6216,6 +6488,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" "use Swoole\\Http\\Status;\n" "\n" "class HttpResponse extends Response\n" @@ -6226,19 +6501,28 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " /** @var string */\n" " protected $reasonPhrase;\n" "\n" - " /** @var array */\n" - " protected $headers = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headers = [];\n" "\n" - " /** @var array */\n" - " protected $headersMap = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headersMap = [];\n" "\n" - " /** @var array */\n" - " protected $setCookieHeaderLines = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $setCookieHeaderLines = [];\n" "\n" + " /**\n" + " * @param array $records\n" + " */\n" " public function __construct(array $records = [])\n" " {\n" " parent::__construct($records);\n" - " $body = (string) $this->getBody();\n" + " $body = $this->getBody();\n" " if (strlen($body) === 0) {\n" " return;\n" " }\n" @@ -6248,17 +6532,17 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return;\n" " }\n" " $headers = explode(\"\\r\\n\", $array[0]);\n" - " $body = $array[1];\n" + " $body = $array[1];\n" " foreach ($headers as $header) {\n" " $array = explode(':', $header, 2); // An array that contains the name and the value of an HTTP header.\n" " if (count($array) != 2) {\n" " continue; // Invalid HTTP header? Ignore it!\n" " }\n" - " $name = trim($array[0]);\n" + " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" - " $statusCode = $array[0];\n" + " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" + " $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" " $this->withSetCookieHeaderLine($value);\n" @@ -6266,8 +6550,8 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " $this->withHeader($name, $value);\n" " }\n" " }\n" - " $statusCode = (int) ($statusCode ?? Status::OK);\n" - " $reasonPhrase = (string) ($reasonPhrase ?? Status::getReasonPhrase($statusCode));\n" + " $statusCode = (int) ($statusCode ?? Status::OK);\n" + " $reasonPhrase = $reasonPhrase ?? Status::getReasonPhrase($statusCode);\n" " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" " $this->withBody($body);\n" " }\n" @@ -6300,6 +6584,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $name ? $this->headers[$name] : null;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getHeaders(): array\n" " {\n" " return $this->headers;\n" @@ -6307,11 +6594,14 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" " public function withHeader(string $name, string $value): self\n" " {\n" - " $this->headers[$name] = $value;\n" + " $this->headers[$name] = $value;\n" " $this->headersMap[strtolower($name)] = $name;\n" " return $this;\n" " }\n" "\n" + " /**\n" + " * @param array $headers\n" + " */\n" " public function withHeaders(array $headers): self\n" " {\n" " foreach ($headers as $name => $value) {\n" @@ -6320,6 +6610,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $this;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getSetCookieHeaderLines(): array\n" " {\n" " return $this->setCookieHeaderLines;\n" @@ -6346,7 +6639,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" "use Swoole\\Coroutine\\Socket;\n" "use Swoole\\FastCGI\\FrameParser;\n" @@ -6358,48 +6651,43 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "class Client\n" "{\n" - " /** @var int */\n" - " protected $af;\n" + " protected int $af;\n" "\n" - " /** @var string */\n" - " protected $host;\n" + " protected string $host;\n" "\n" - " /** @var int */\n" - " protected $port;\n" + " protected int $port;\n" "\n" - " /** @var bool */\n" - " protected $ssl;\n" + " protected bool $ssl;\n" "\n" - " /** @var Socket */\n" - " protected $socket;\n" + " protected ?Socket $socket;\n" "\n" " public function __construct(string $host, int $port = 0, bool $ssl = false)\n" " {\n" " if (stripos($host, 'unix:/') === 0) {\n" " $this->af = AF_UNIX;\n" - " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" - " $port = 0;\n" - " } elseif (strpos($host, ':') !== false) {\n" + " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" + " $port = 0;\n" + " } elseif (str_contains($host, ':')) {\n" " $this->af = AF_INET6;\n" " } else {\n" " $this->af = AF_INET;\n" " }\n" " $this->host = $host;\n" " $this->port = $port;\n" - " $this->ssl = $ssl;\n" + " $this->ssl = $ssl;\n" " }\n" "\n" " /**\n" + " * @return ($request is HttpRequest ? HttpResponse : Response)\n" " * @throws Exception\n" - " * @return HttpResponse|Response\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" - " if (!$this->socket) {\n" + " if (!isset($this->socket)) {\n" " $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\n" " $socket->setProtocol([\n" - " 'open_ssl' => $this->ssl,\n" - " 'open_fastcgi_protocol' => true,\n" + " Constant::OPTION_OPEN_SSL => $this->ssl,\n" + " Constant::OPTION_OPEN_FASTCGI_PROTOCOL => true,\n" " ]);\n" " if (!$socket->connect($this->host, $this->port, $timeout)) {\n" " $this->ioException();\n" @@ -6413,33 +6701,17 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" " $records = [];\n" " while (true) {\n" - " if (SWOOLE_VERSION_ID < 40500) {\n" - " $recvData = '';\n" - " while (true) {\n" - " $tmp = $socket->recv(8192, $timeout);\n" - " if (!$tmp) {\n" - " if ($tmp === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " $recvData .= $tmp;\n" - " if (FrameParser::hasFrame($recvData)) {\n" - " break;\n" - " }\n" - " }\n" - " } else {\n" - " $recvData = $socket->recvPacket($timeout);\n" - " if (!$recvData) {\n" - " if ($recvData === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " if (!FrameParser::hasFrame($recvData)) {\n" - " $this->ioException(SOCKET_EPROTO);\n" + " $recvData = $socket->recvPacket($timeout);\n" + " if (!$recvData) {\n" + " if ($recvData === '') {\n" + " $this->ioException(SOCKET_ECONNRESET);\n" " }\n" + " $this->ioException();\n" + " }\n" + " if (!FrameParser::hasFrame($recvData)) {\n" + " $this->ioException(SOCKET_EPROTO);\n" " }\n" + "\n" " do {\n" " $records[] = $record = FrameParser::parseFrame($recvData);\n" " } while (strlen($recvData) !== 0);\n" @@ -6448,27 +6720,24 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $this->socket->close();\n" " $this->socket = null;\n" " }\n" - " switch (true) {\n" - " case $request instanceof HttpRequest:\n" - " return new HttpResponse($records);\n" - " default:\n" - " return new Response($records);\n" - " }\n" + " // @phpstan-ignore argument.type,argument.type\n" + " return ($request instanceof HttpRequest) ? new HttpResponse($records) : new Response($records);\n" " }\n" " }\n" - " /* never here */\n" - " exit(1);\n" + "\n" + " // Code execution should never reach here. However, we still put an exit() statement here for safe purpose.\n" + " exit(1); // @phpstan-ignore deadCode.unreachable\n" " }\n" "\n" " public static function parseUrl(string $url): array\n" " {\n" - " $url = parse_url($url);\n" + " $url = parse_url($url);\n" " $host = $url['host'] ?? '';\n" " $port = $url['port'] ?? 0;\n" " if (empty($host)) {\n" " $host = $url['path'] ?? '';\n" " if (empty($host)) {\n" - " throw new InvalidArgumentException('Invalid url');\n" + " throw new \\InvalidArgumentException('Invalid url');\n" " }\n" " $host = \"unix:/{$host}\";\n" " }\n" @@ -6477,15 +6746,15 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" " public static function call(string $url, string $path, $data = '', float $timeout = -1): string\n" " {\n" - " $client = new Client(...static::parseUrl($url));\n" - " $pathInfo = parse_url($path);\n" - " $path = $pathInfo['path'] ?? '';\n" - " $root = dirname($path);\n" - " $scriptName = '/' . basename($path);\n" + " $client = new Client(...static::parseUrl($url));\n" + " $pathInfo = parse_url($path);\n" + " $path = $pathInfo['path'] ?? '';\n" + " $root = dirname($path);\n" + " $scriptName = '/' . basename($path);\n" " $documentUri = $scriptName;\n" - " $query = $pathInfo['query'] ?? '';\n" - " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" - " $request = new HttpRequest();\n" + " $query = $pathInfo['query'] ?? '';\n" + " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" + " $request = new HttpRequest();\n" " $request->withDocumentRoot($root)\n" " ->withScriptFilename($path)\n" " ->withScriptName($documentUri)\n" @@ -6493,7 +6762,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " ->withRequestUri($requestUri)\n" " ->withQueryString($query)\n" " ->withBody($data)\n" - " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST');\n" + " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST')\n" + " ;\n" " $response = $client->execute($request, $timeout);\n" " return $response->getBody();\n" " }\n" @@ -6503,7 +6773,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $socket = $this->socket;\n" " if ($errno !== null) {\n" " $socket->errCode = $errno;\n" - " $socket->errMsg = swoole_strerror($errno);\n" + " $socket->errMsg = swoole_strerror($errno);\n" " }\n" " $socket->close();\n" " $this->socket = null;\n" @@ -6543,10 +6813,11 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\HttpRequest;\n" "use Swoole\\FastCGI\\HttpResponse;\n" "use Swoole\\Http;\n" + "use Swoole\\Http\\Request as SwooleHttpRequest;\n" + "use Swoole\\Http\\Response as SwooleHttpResponse;\n" "\n" "class Proxy\n" "{\n" @@ -6577,8 +6848,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " public function __construct(string $url, string $documentRoot = '/')\n" " {\n" " [$this->host, $this->port] = Client::parseUrl($url);\n" - " $this->documentRoot = $documentRoot;\n" - " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" + " $this->documentRoot = $documentRoot;\n" + " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" " }\n" "\n" " public function withTimeout(float $timeout): self\n" @@ -6639,67 +6910,60 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return $this;\n" " }\n" "\n" - " public function translateRequest($userRequest): HttpRequest\n" + " public function translateRequest(SwooleHttpRequest $userRequest): HttpRequest\n" " {\n" - " $request = new HttpRequest();\n" - " if ($userRequest instanceof \\Swoole\\Http\\Request) {\n" - " $server = $userRequest->server;\n" - " $headers = $userRequest->header;\n" - " $pathInfo = $userRequest->server['path_info'];\n" - " $pathInfo = '/' . (ltrim($pathInfo, '/'));\n" - " if (strlen($this->index) !== 0) {\n" - " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" - " if (empty($extension)) {\n" - " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" - " }\n" + " $server = $userRequest->server;\n" + " $headers = $userRequest->header;\n" + " $pathInfo = $userRequest->server['path_info'];\n" + " $pathInfo = '/' . ltrim($pathInfo, '/');\n" + " if (strlen($this->index) !== 0) {\n" + " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" + " if (empty($extension)) {\n" + " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" " }\n" - " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" - " $queryString = $server['query_string'] ?? '';\n" - " if (strlen($queryString) !== 0) {\n" - " $requestUri .= \"?{$server['query_string']}\";\n" - " }\n" - " $request\n" - " ->withDocumentRoot($this->documentRoot)\n" - " ->withScriptFilename($this->documentRoot . $pathInfo)\n" - " ->withScriptName($scriptName)\n" - " ->withDocumentUri($documentUri)\n" - " ->withServerProtocol($server['server_protocol'])\n" - " ->withServerAddr('127.0.0.1')\n" - " ->withServerPort($server['server_port'])\n" - " ->withRemoteAddr($server['remote_addr'])\n" - " ->withRemotePort($server['remote_port'])\n" - " ->withMethod($server['request_method'])\n" - " ->withRequestUri($requestUri)\n" - " ->withQueryString($queryString)\n" - " ->withContentType($headers['content-type'] ?? '')\n" - " ->withContentLength((int) ($headers['content-length'] ?? 0))\n" - " ->withHeaders($headers)\n" - " ->withBody($userRequest->rawContent())\n" - " ->withAddedParams($this->params);\n" - " if ($this->https) {\n" - " $request->withParam('HTTPS', '1');\n" - " }\n" - " } else {\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userRequest));\n" " }\n" + " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" + " $queryString = $server['query_string'] ?? '';\n" + " if (strlen($queryString) !== 0) {\n" + " $requestUri .= \"?{$server['query_string']}\";\n" + " }\n" + " $request = (new HttpRequest())\n" + " ->withDocumentRoot($this->documentRoot)\n" + " ->withScriptFilename($this->documentRoot . $pathInfo)\n" + " ->withScriptName($scriptName)\n" + " ->withDocumentUri($documentUri)\n" + " ->withServerProtocol($server['server_protocol'])\n" + " ->withServerAddr('127.0.0.1')\n" + " ->withServerPort($server['server_port'])\n" + " ->withRemoteAddr($server['remote_addr'])\n" + " ->withRemotePort($server['remote_port'])\n" + " ->withMethod($server['request_method'])\n" + " ->withRequestUri($requestUri)\n" + " ->withQueryString($queryString)\n" + " ->withContentType($headers['content-type'] ?? '')\n" + " ->withContentLength((int) ($headers['content-length'] ?? 0))\n" + " ->withHeaders($headers)\n" + " ->withBody($userRequest->rawContent())\n" + " ->withAddedParams($this->params)\n" + " ;\n" + " if ($this->https) {\n" + " $request->withParam('HTTPS', '1');\n" + " }\n" + "\n" " return $request;\n" " }\n" "\n" - " public function translateResponse(HttpResponse $response, $userResponse): void\n" + " public function translateResponse(HttpResponse $response, SwooleHttpResponse $userResponse): void\n" " {\n" - " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" - " $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());\n" - " $userResponse->header = $response->getHeaders();\n" - " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" - " $userResponse->end($response->getBody());\n" - " } else {\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" - " }\n" + " $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());\n" + " $userResponse->header = $response->getHeaders();\n" + " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" + " $userResponse->end($response->getBody());\n" " }\n" "\n" - " public function pass($userRequest, $userResponse): void\n" + " public function pass(SwooleHttpRequest|HttpRequest $userRequest, SwooleHttpResponse $userResponse): void\n" " {\n" - " if (!($userRequest instanceof HttpRequest)) {\n" + " if (!$userRequest instanceof HttpRequest) {\n" " $request = $this->translateRequest($userRequest);\n" " } else {\n" " $request = $userRequest;\n" @@ -6711,28 +6975,29 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return;\n" " }\n" " }\n" - " $client = new Client($this->host, $this->port);\n" - " $response = $client->execute($request, $this->timeout);\n" + " $response = (new Client($this->host, $this->port))->execute($request, $this->timeout);\n" " $this->translateResponse($response, $userResponse);\n" " }\n" "\n" - " /* @return bool ['hit' => true, 'miss' => false] */\n" - " public function staticFileFiltrate(HttpRequest $request, $userResponse): bool\n" + " /**\n" + " * Send content of a static file to the client, if the file is accessible and is not a PHP file.\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" + " * accessible even the return value is true.\n" + " */\n" + " public function staticFileFiltrate(HttpRequest $request, SwooleHttpResponse $userResponse): bool\n" " {\n" - " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" - " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" - " if ($extension !== 'php') {\n" - " $realPath = realpath($request->getScriptFilename());\n" - " if (!$realPath || strpos($realPath, $this->documentRoot) !== 0 || !is_file($realPath)) {\n" - " $userResponse->status(Http\\Status::NOT_FOUND);\n" - " } else {\n" - " $userResponse->sendfile($realPath);\n" - " }\n" - " return true;\n" + " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" + " if ($extension !== 'php') {\n" + " $realPath = realpath($request->getScriptFilename());\n" + " if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\n" + " $userResponse->status(Http\\Status::NOT_FOUND);\n" + " } else {\n" + " $userResponse->sendfile($realPath);\n" " }\n" - " return false;\n" + " return true;\n" " }\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " return false;\n" " }\n" "}\n"; @@ -6751,6 +7016,7 @@ static const char* swoole_library_source_core_process_manager = "namespace Swoole\\Process;\n" "\n" "use Swoole\\Constant;\n" + "\n" "use function Swoole\\Coroutine\\run;\n" "\n" "class Manager\n" @@ -6847,14 +7113,11 @@ static const char* swoole_library_source_core_server_admin = "\n" "namespace Swoole\\Server;\n" "\n" - "use Reflection;\n" - "use ReflectionClass;\n" - "use ReflectionExtension;\n" "use Swoole\\Coroutine;\n" - "use Swoole\\Coroutine\\System;\n" "use Swoole\\Http\\Request;\n" "use Swoole\\Http\\Response;\n" "use Swoole\\Server;\n" + "use Swoole\\StringObject;\n" "use Swoole\\Timer;\n" "\n" "class Admin\n" @@ -6878,9 +7141,35 @@ static const char* swoole_library_source_core_server_admin = "\n" " public const SIZE_OF_ZEND_ARRAY = 56;\n" "\n" - " public const DASHBOARD_DIR = '/opt/swoole/dashboard';\n" + " private static array $map = [\n" + " 'reactor' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" + " 'reactor_thread' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" + " 'worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" + " 'event_worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" + " 'task' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" + " 'task_worker' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" + " ];\n" + "\n" + " private static array $allList = [\n" + " 'all',\n" + " 'all_reactor',\n" + " 'all_reactor_thread',\n" + " 'all_worker',\n" + " 'all_event_worker',\n" + " 'all_task',\n" + " 'all_task_worker',\n" + " 'specific',\n" + " ];\n" + "\n" + " private static array $postMethodList = [\n" + " 'server_reload',\n" + " 'server_shutdown',\n" + " 'close_session',\n" + " ];\n" + "\n" + " private static string $accessToken = '';\n" "\n" - " public static function init(Server $server)\n" + " public static function init(Server $server): void\n" " {\n" " $accepted_process_types = SWOOLE_SERVER_COMMAND_MASTER |\n" " SWOOLE_SERVER_COMMAND_MANAGER |\n" @@ -6890,7 +7179,7 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_reload',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $server->reload();\n" " return self::json('Operation succeeded');\n" " }\n" @@ -6899,7 +7188,7 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_shutdown',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg): void {\n" " $server->shutdown();\n" " }\n" " );\n" @@ -6907,26 +7196,22 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'coroutine_stats',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(Coroutine::stats());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(Coroutine::stats())\n" " );\n" "\n" " $server->addCommand(\n" " 'coroutine_list',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(iterator_to_array(Coroutine::list()));\n" - " }\n" + " fn (Server $server, string $msg) => self::json(iterator_to_array(Coroutine::list()))\n" " );\n" "\n" " $server->addCommand(\n" " 'coroutine_bt',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " $json = json_decode($msg);\n" - " $cid = empty($json->cid) ? 0 : intval($json->cid);\n" - " $bt = Coroutine::getBackTrace($cid);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, null, 512, JSON_THROW_ON_ERROR);\n" + " $cid = empty($json->cid) ? 0 : intval($json->cid);\n" + " $bt = Coroutine::getBackTrace($cid);\n" " if ($bt === false) {\n" " return self::json(\"Coroutine#{$cid} not exists\", 4004);\n" " }\n" @@ -6937,23 +7222,18 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_stats',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json($server->stats());\n" - " }\n" + " fn (Server $server, string $msg) => self::json($server->stats())\n" " );\n" "\n" " $server->addCommand(\n" " 'server_setting',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " /**\n" - " * @var Server $server\n" - " */\n" - " $setting = $server->setting;\n" - " $setting['mode'] = $server->mode;\n" - " $setting['host'] = $server->host;\n" - " $setting['port'] = $server->port;\n" - " $setting['master_pid'] = $server->master_pid;\n" + " function (Server $server, string $msg) {\n" + " $setting = $server->setting;\n" + " $setting['mode'] = $server->mode;\n" + " $setting['host'] = $server->host;\n" + " $setting['port'] = $server->port;\n" + " $setting['master_pid'] = $server->master_pid;\n" " $setting['manager_pid'] = $server->manager_pid;\n" " return self::json($setting);\n" " }\n" @@ -6962,8 +7242,8 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_client_info',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['session_id'])) {\n" " return self::json('require session_id', 4003);\n" " }\n" @@ -6971,86 +7251,76 @@ static const char* swoole_library_source_core_server_admin = " }\n" " );\n" "\n" - " $server->addCommand('close_session', $accepted_process_types, [__CLASS__, 'handlerCloseSession']);\n" - " $server->addCommand('get_version_info', $accepted_process_types, [__CLASS__, 'handlerGetVersionInfo']);\n" - " $server->addCommand('get_worker_info', $accepted_process_types, [__CLASS__, 'handlerGetWorkerInfo']);\n" - " $server->addCommand('get_timer_list', $accepted_process_types, [__CLASS__, 'handlerGetTimerList']);\n" - " $server->addCommand('get_coroutine_list', $accepted_process_types, [__CLASS__, 'handlerGetCoroutineList']);\n" - " $server->addCommand('get_objects', $accepted_process_types, [__CLASS__, 'handlerGetObjects']);\n" - " $server->addCommand('get_class_info', $accepted_process_types, [__CLASS__, 'handlerGetClassInfo']);\n" - " $server->addCommand('get_function_info', $accepted_process_types, [__CLASS__, 'handlerGetFunctionInfo']);\n" - " $server->addCommand('get_object_by_handle', $accepted_process_types, [__CLASS__, 'handlerGetObjectByHandle']);\n" - " $server->addCommand('get_server_cpu_usage', $accepted_process_types, [__CLASS__, 'handlerGetServerCpuUsage']);\n" + " $server->addCommand('close_session', $accepted_process_types, [self::class, 'handlerCloseSession']);\n" + " $server->addCommand('get_version_info', $accepted_process_types, [self::class, 'handlerGetVersionInfo']);\n" + " $server->addCommand('get_worker_info', $accepted_process_types, [self::class, 'handlerGetWorkerInfo']);\n" + " $server->addCommand('get_timer_list', $accepted_process_types, [self::class, 'handlerGetTimerList']);\n" + " $server->addCommand('get_coroutine_list', $accepted_process_types, [self::class, 'handlerGetCoroutineList']);\n" + " $server->addCommand('get_objects', $accepted_process_types, [self::class, 'handlerGetObjects']);\n" + " $server->addCommand('get_class_info', $accepted_process_types, [self::class, 'handlerGetClassInfo']);\n" + " $server->addCommand('get_function_info', $accepted_process_types, [self::class, 'handlerGetFunctionInfo']);\n" + " $server->addCommand('get_object_by_handle', $accepted_process_types, [self::class, 'handlerGetObjectByHandle']);\n" + " $server->addCommand('get_server_cpu_usage', $accepted_process_types, [self::class, 'handlerGetServerCpuUsage']);\n" " $server->addCommand(\n" " 'get_server_memory_usage',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetServerMemoryUsage']\n" + " [self::class, 'handlerGetServerMemoryUsage']\n" " );\n" " $server->addCommand(\n" " 'get_static_property_value',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetStaticPropertyValue']\n" + " [self::class, 'handlerGetStaticPropertyValue']\n" " );\n" " $server->addCommand(\n" " 'get_defined_functions',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetDefinedFunctions']\n" + " [self::class, 'handlerGetDefinedFunctions']\n" " );\n" - " $server->addCommand('get_declared_classes', $accepted_process_types, [__CLASS__, 'handlerGetDeclaredClasses']);\n" + " $server->addCommand('get_declared_classes', $accepted_process_types, [self::class, 'handlerGetDeclaredClasses']);\n" "\n" - " if (PHP_VERSION_ID > 70300) {\n" - " $server->addCommand(\n" - " 'gc_status',\n" - " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(gc_status());\n" - " }\n" - " );\n" - " }\n" + " $server->addCommand(\n" + " 'gc_status',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " return self::json(gc_status());\n" + " }\n" + " );\n" "\n" " if (extension_loaded('opcache')) {\n" " $server->addCommand(\n" " 'opcache_status',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(opcache_get_status(true));\n" - " }\n" + " fn (Server $server, string $msg) => self::json(opcache_get_status(true))\n" " );\n" " }\n" "\n" " $server->addCommand(\n" " 'getpid',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(['pid' => posix_getpid()]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\n" " );\n" "\n" " $server->addCommand(\n" " 'memory_usage',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json([\n" - " 'usage' => memory_get_usage(),\n" - " 'real_usage' => memory_get_usage(true),\n" - " ]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json([\n" + " 'usage' => memory_get_usage(),\n" + " 'real_usage' => memory_get_usage(true),\n" + " ])\n" " );\n" "\n" " $server->addCommand(\n" " 'get_included_files',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(['files' => get_included_files()]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json(['files' => get_included_files()])\n" " );\n" "\n" - " $server->addCommand('get_resources', $accepted_process_types, [__CLASS__, 'handlerGetResources']);\n" + " $server->addCommand('get_resources', $accepted_process_types, [self::class, 'handlerGetResources']);\n" "\n" " $server->addCommand(\n" " 'get_defined_constants',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $constants = get_defined_constants();\n" " foreach ($constants as $k => $c) {\n" " if (is_resource($c)) {\n" @@ -7065,15 +7335,15 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_loaded_extensions',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $extensions = get_loaded_extensions();\n" - " $list = [];\n" + " $list = [];\n" " foreach ($extensions as $key => $extension) {\n" - " $ext = new \\ReflectionExtension($extension);\n" + " $ext = new \\ReflectionExtension($extension);\n" " $list[$key] = [\n" - " 'id' => ++$key,\n" - " 'name' => $extension,\n" - " 'version' => $ext->getVersion() ?? '',\n" + " 'id' => ++$key,\n" + " 'name' => $extension,\n" + " 'version' => (string) $ext->getVersion(),\n" " ];\n" " }\n" " return self::json($list);\n" @@ -7083,24 +7353,20 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_declared_interfaces',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(get_declared_interfaces());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(get_declared_interfaces())\n" " );\n" "\n" " $server->addCommand(\n" " 'get_declared_traits',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(get_declared_traits());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(get_declared_traits())\n" " );\n" "\n" " $server->addCommand(\n" " 'get_included_file_contents',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['filename'])) {\n" " return self::json('require filename', 4003);\n" " }\n" @@ -7120,18 +7386,18 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_globals',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $globals = [];\n" " foreach ($GLOBALS as $key => $item) {\n" " if ($key === 'GLOBALS') {\n" " continue;\n" " }\n" - " $type = gettype($item);\n" + " $type = gettype($item);\n" " $other = [];\n" " if ($type === 'object') {\n" " $other = [\n" - " 'class_name' => get_class($item),\n" - " 'object_id' => spl_object_id($item),\n" + " 'class_name' => $item::class,\n" + " 'object_id' => spl_object_id($item),\n" " 'object_hash' => spl_object_hash($item),\n" " ];\n" " }\n" @@ -7139,9 +7405,9 @@ static const char* swoole_library_source_core_server_admin = " $item = '';\n" " }\n" " $globals[] = [\n" - " 'key' => $key,\n" + " 'key' => $key,\n" " 'value' => $item,\n" - " 'type' => $type,\n" + " 'type' => $type,\n" " 'other' => $other,\n" " ];\n" " }\n" @@ -7152,14 +7418,14 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_extension_info',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" "\n" " if (empty($json['extension_name']) || !extension_loaded($json['extension_name'])) {\n" " return self::json('require extension_name', 4004);\n" " }\n" "\n" - " $ext = new ReflectionExtension($json['extension_name']);\n" + " $ext = new \\ReflectionExtension($json['extension_name']);\n" "\n" " ob_start();\n" " $ext->info();\n" @@ -7175,25 +7441,73 @@ static const char* swoole_library_source_core_server_admin = " unset($constants['NULL'], $constants['NAN'], $constants['INF']);\n" "\n" " return self::json([\n" - " 'classes' => $ext->getClassNames(),\n" - " 'version' => $ext->getVersion(),\n" - " 'constants' => $constants,\n" - " 'ini_entries' => $ext->getINIEntries(),\n" + " 'classes' => $ext->getClassNames(),\n" + " 'version' => $ext->getVersion(),\n" + " 'constants' => $constants,\n" + " 'ini_entries' => $ext->getINIEntries(),\n" " 'dependencies' => $ext->getDependencies(),\n" - " 'functions' => array_keys($ext->getFunctions()),\n" - " 'info' => trim($info),\n" + " 'functions' => array_keys($ext->getFunctions()),\n" + " 'info' => trim($info),\n" " ]);\n" " }\n" " );\n" + "\n" + " $server->addCommand(\n" + " 'get_composer_packages',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " if (!class_exists(\\Composer\\InstalledVersions::class)) {\n" + " return self::json('require composer 2.0', 4003);\n" + " }\n" + "\n" + " $list = [];\n" + " foreach (['getAllRawData', 'getRawData'] as $method) {\n" + " if (!method_exists(\\Composer\\InstalledVersions::class, $method)) {\n" + " continue;\n" + " }\n" + "\n" + " $raw_data[] = \\Composer\\InstalledVersions::$method();\n" + " if ($method === 'getAllRawData') {\n" + " $raw_data = \\Composer\\InstalledVersions::$method();\n" + " array_shift($raw_data);\n" + " }\n" + "\n" + " foreach ($raw_data as $key => $package) {\n" + " $key_name = $package['root']['name'];\n" + " if ($package['root']['name'] === '__root__' && isset($list['__root__'])) {\n" + " $key_name = \"__root__{$key}\";\n" + " }\n" + " $package['root']['install_path'] = !empty($package['root']['install_path']) ? realpath($package['root']['install_path']) : '';\n" + " $list[$key_name] = $package;\n" + " }\n" + " break;\n" + " }\n" + " return self::json($list);\n" + " }\n" + " );\n" " }\n" "\n" - " public static function start(Server $server)\n" + " public static function getAccessToken(): string\n" + " {\n" + " return self::$accessToken;\n" + " }\n" + "\n" + " public static function start(Server $server): void\n" " {\n" " $admin_server_uri = swoole_string($server->setting['admin_server']);\n" " if ($admin_server_uri->startsWith('unix:/')) {\n" - " return swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " return;\n" " }\n" - " [$host, $port] = $admin_server_uri->split(':', 2)->toArray();\n" + "\n" + " if ($admin_server_uri->contains('@')) {\n" + " [$access_name, $access_secret] = $admin_server_uri->split('@', 2)->get(0)->split(':', 2)->toArray();\n" + " self::$accessToken = sha1($access_name . $access_secret);\n" + " [$host, $port] = $admin_server_uri->split('@', 2)->get(1)->split(':', 2)->toArray();\n" + " } else {\n" + " [$host, $port] = $admin_server_uri->split(':', 2)->toArray();\n" + " }\n" + "\n" " $admin_server = new Coroutine\\Http\\Server($host, intval($port));\n" "\n" " $admin_server->handle('/api', function (Request $req, Response $resp) use ($server) {\n" @@ -7204,7 +7518,53 @@ static const char* swoole_library_source_core_server_admin = " return;\n" " }\n" "\n" + " $resp->header('Server', 'swoole-admin-server');\n" + " $resp->header('Access-Control-Allow-Origin', '*');\n" + " $resp->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n" + " $resp->header('Access-Control-Allow-Headers', 'X-ACCESS-TOKEN, X-ADMIN-SERVER-ACCESS-TOKEN');\n" + "\n" + " $method = $req->getMethod();\n" + "\n" + " if ($method === 'OPTIONS') {\n" + " $resp->end();\n" + " return;\n" + " }\n" + "\n" + " $token = self::getAccessToken();\n" + " if (!empty($token)) {\n" + " $token_header = $req->header['x-admin-server-access-token'] ?? '';\n" + " if ($token_header !== $token) {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad access token', 4003));\n" + " return;\n" + " }\n" + " }\n" + "\n" " $cmd = $path_array->get(1)->toString();\n" + "\n" + " if (in_array($cmd, self::$postMethodList) && $method != 'POST') {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad request method', 4003));\n" + " return;\n" + " }\n" + "\n" + " if ($method == 'GET') {\n" + " $data = $req->get;\n" + " } else {\n" + " $data = $req->post;\n" + " }\n" + "\n" + " if ($cmd === 'multi') {\n" + " $body = json_decode($req->getContent(), true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($body) || !is_array($body) || $method != 'POST') {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $result = self::handlerMulti($server, $body);\n" + " $resp->end(json_encode($result, JSON_INVALID_UTF8_IGNORE));\n" + " return;\n" + " }\n" + "\n" " if ($path_array->count() == 2) {\n" " $process = swoole_string('master');\n" " } else {\n" @@ -7213,10 +7573,19 @@ static const char* swoole_library_source_core_server_admin = "\n" " if ($process->startsWith('master')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" " } elseif ($process->startsWith('manager')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('all') || $process->equals('specific')) {\n" + " if (!in_array($process->toString(), self::$allList)) {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $result = self::handlerGetAll($server, $process, $cmd, $data);\n" + "\n" + " $resp->end(self::json($result));\n" + " return;\n" " } else {\n" " $array = $process->split('-');\n" " if ($array->count() != 2) {\n" @@ -7226,44 +7595,25 @@ static const char* swoole_library_source_core_server_admin = " return;\n" " }\n" "\n" - " static $map = [\n" - " 'reactor' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" - " 'reactor_thread' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" - " 'worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" - " 'event_worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" - " 'task' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" - " 'task_worker' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" - " ];\n" - "\n" - " if (!isset($map[$array->get(0)->toString()])) {\n" + " if (!isset(self::$map[$array->get(0)->toString()])) {\n" " goto _bad_process;\n" " }\n" "\n" - " $process_type = $map[$array->get(0)->toString()];\n" - " $process_id = intval($array->get(1)->toString());\n" - " }\n" - "\n" - " if ($req->getMethod() == 'GET') {\n" - " $data = $req->get;\n" - " } else {\n" - " $data = $req->post;\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" " }\n" "\n" - " $resp->header('Access-Control-Allow-Origin', '*');\n" - " $resp->header('Access-Control-Allow-Methods', 'GET, OPTIONS');\n" - " $resp->header('Access-Control-Allow-Headers', 'X-ACCESS-TOKEN');\n" - "\n" - " $result = $server->command($cmd, intval($process_id), intval($process_type), $data, false);\n" + " $result = $server->command($cmd, $process_id, intval($process_type), $data, false);\n" " if (!$result) {\n" " $resp->end(json_encode([\n" " 'code' => swoole_last_error(),\n" " 'data' => swoole_strerror(swoole_last_error()),\n" - " ]));\n" + " ], JSON_THROW_ON_ERROR));\n" " } else {\n" " $resp->end($result);\n" " }\n" " });\n" - " $admin_server->handle('/', function (Request $req, Response $resp) use ($server) {\n" + " $admin_server->handle('/', function (Request $req, Response $resp): void {\n" " $resp->status(404);\n" " });\n" " $server->admin_server = $admin_server;\n" @@ -7271,17 +7621,15 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server Server\n" - " * @param $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetResources($server, $msg)\n" + " public static function handlerGetResources(Server $server, string $msg)\n" " {\n" " $resources = get_resources();\n" - " $list = [];\n" + " $list = [];\n" " foreach ($resources as $r) {\n" " $info = [\n" - " 'id' => function_exists('get_resource_id') ? get_resource_id($r) : intval($r),\n" + " 'id' => get_resource_id($r),\n" " 'type' => get_resource_type($r),\n" " ];\n" " if ($info['type'] == 'stream') {\n" @@ -7293,36 +7641,30 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server Server\n" - " * @param $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetWorkerInfo($server, $msg)\n" + " public static function handlerGetWorkerInfo(Server $server, string $msg)\n" " {\n" " $info = [\n" - " 'id' => $server->getWorkerId(),\n" - " 'pid' => $server->getWorkerPid(),\n" - " 'gc_status' => gc_status(),\n" - " 'memory_usage' => memory_get_usage(),\n" + " 'id' => $server->getWorkerId(),\n" + " 'pid' => $server->getWorkerPid(),\n" + " 'gc_status' => gc_status(),\n" + " 'memory_usage' => memory_get_usage(),\n" " 'memory_real_usage' => memory_get_usage(true),\n" - " 'process_status' => self::getProcessStatus(),\n" - " 'coroutine_stats' => Coroutine::stats(),\n" - " 'timer_stats' => Timer::stats(),\n" + " 'process_status' => self::getProcessStatus(),\n" + " 'coroutine_stats' => Coroutine::stats(),\n" + " 'timer_stats' => Timer::stats(),\n" + " 'vm_status' => swoole_get_vm_status(),\n" " ];\n" - " if (function_exists('swoole_get_vm_status')) {\n" - " $info['vm_status'] = swoole_get_vm_status();\n" - " }\n" " return self::json($info);\n" " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" " * @return false|string\n" " */\n" - " public static function handlerCloseSession($server, $msg)\n" + " public static function handlerCloseSession(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['session_id'])) {\n" " return self::json('require session_id', 4003);\n" " }\n" @@ -7333,16 +7675,14 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetTimerList($server, $msg)\n" + " public static function handlerGetTimerList(Server $server, string $msg)\n" " {\n" " $list = [];\n" " foreach (Timer::list() as $timer_id) {\n" " $list[] = [\n" - " 'id' => $timer_id,\n" + " 'id' => $timer_id,\n" " 'info' => Timer::info($timer_id),\n" " ];\n" " }\n" @@ -7351,43 +7691,38 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetCoroutineList($server, $msg)\n" + " public static function handlerGetCoroutineList(Server $server, string $msg)\n" " {\n" " $list = [];\n" " foreach (Coroutine::list() as $cid) {\n" " $list[] = [\n" - " 'id' => $cid,\n" - " 'elapsed' => Coroutine::getElapsed($cid),\n" + " 'id' => $cid,\n" + " 'elapsed' => Coroutine::getElapsed($cid),\n" " 'stack_usage' => Coroutine::getStackUsage($cid),\n" - " 'backTrace' => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\n" + " 'backTrace' => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\n" " ];\n" " }\n" "\n" " return self::json($list);\n" " }\n" "\n" - " public static function handlerGetObjects($server, $msg)\n" + " public static function handlerGetObjects(Server $server, string $msg)\n" " {\n" - " if (!function_exists('swoole_get_objects')) {\n" - " return self::json(['require ext-swoole_plus'], 5000);\n" - " }\n" - " $list = [];\n" + " $list = [];\n" " $objects = swoole_get_objects();\n" " foreach ($objects as $o) {\n" - " $class_name = get_class($o);\n" - " $class = new ReflectionClass($class_name);\n" - " $filename = $class->getFileName();\n" - " $line = $class->getStartLine();\n" - " $list[] = [\n" - " 'id' => spl_object_id($o),\n" - " 'hash' => spl_object_hash($o),\n" - " 'class' => $class_name,\n" - " 'filename' => $filename ?: '',\n" - " 'line' => $line ?: '',\n" + " $class_name = $o::class;\n" + " $class = new \\ReflectionClass($class_name);\n" + " $filename = $class->getFileName();\n" + " $line = $class->getStartLine();\n" + " $list[] = [\n" + " 'id' => spl_object_id($o),\n" + " 'hash' => spl_object_hash($o),\n" + " 'class' => $class_name,\n" + " 'filename' => $filename ?: '',\n" + " 'line' => $line ?: '',\n" " 'memory_size' => self::getObjectMemorySize($o),\n" " ];\n" " }\n" @@ -7395,18 +7730,24 @@ static const char* swoole_library_source_core_server_admin = " return self::json($list);\n" " }\n" "\n" - " public static function handlerGetClassInfo($server, $msg)\n" + " public static function handlerGetClassInfo(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" - " if (empty($json['class_name'])) {\n" - " return self::json(['error' => 'require class_name'], 4004);\n" - " }\n" - "\n" - " if (!class_exists($json['class_name'], false)) {\n" - " return self::json(\"{$json['class_name']} not exists\", 4003);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (!empty($json['class_name'])) {\n" + " if (!class_exists($json['class_name'], false) && !interface_exists($json['class_name'], false)) {\n" + " return self::json(\"{$json['class_name']} not exists\", 4003);\n" + " }\n" + " $name = $json['class_name'];\n" + " } elseif (!empty($json['interface_name'])) {\n" + " if (!interface_exists($json['interface_name'], false)) {\n" + " return self::json(\"{$json['interface_name']} not exists\", 4003);\n" + " }\n" + " $name = $json['interface_name'];\n" + " } else {\n" + " return self::json(['error' => 'require class_name or interface_name'], 4004);\n" " }\n" "\n" - " $class = new ReflectionClass($json['class_name']);\n" + " $class = new \\ReflectionClass($name);\n" "\n" " $filename = $class->getFileName();\n" "\n" @@ -7414,147 +7755,161 @@ static const char* swoole_library_source_core_server_admin = " $tmp = [];\n" " foreach ($data as $k => $v) {\n" " $tmp[] = [\n" - " 'name' => $k,\n" + " 'name' => $k,\n" " 'value' => is_array($v) ? var_export($v, true) : $v,\n" - " 'type' => is_array($v) ? 'detail' : 'default',\n" + " 'type' => is_array($v) ? 'detail' : 'default',\n" " ];\n" " }\n" " return $tmp;\n" " };\n" "\n" " $tmpConstants = $class->getConstants();\n" - " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" + " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" "\n" " $staticProperties = [];\n" - " $properties = [];\n" - " $tmpProperties = $class->getProperties();\n" + " $properties = [];\n" + " $tmpProperties = $class->getProperties();\n" "\n" " $getTmpProperties = function ($class, $data) {\n" - " $static = [];\n" - " $no_static = [];\n" - " $reflProp = $class->getDefaultProperties();\n" + " $static = [];\n" + " $noStatic = [];\n" + " $defaultProperties = $class->getDefaultProperties();\n" " foreach ($data as $k => $v) {\n" - " $name = $v->getName();\n" - " $modifiers = Reflection::getModifierNames($v->getModifiers());\n" + " $name = $v->getName();\n" + " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" - " 'name' => $name,\n" - " 'value' => $reflProp[$name],\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " } else {\n" - " $no_static[] = [\n" - " 'name' => $name,\n" - " 'value' => $reflProp[$name],\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " }\n" " }\n" - " return ['static' => $static, 'no_static' => $no_static];\n" + " return ['static' => $static, 'no_static' => $noStatic];\n" " };\n" "\n" " if ($tmpProperties) {\n" - " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" + " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" " $staticProperties = $tmpProperties['static'];\n" - " $properties = $tmpProperties['no_static'];\n" + " $properties = $tmpProperties['no_static'];\n" " }\n" "\n" - " $staticMethods = [];\n" - " $methods = [];\n" + " $staticMethods = [];\n" + " $methods = [];\n" " $tmpStaticMethods = $class->getMethods();\n" "\n" " $getTmpMethods = function ($data) {\n" - " $static = [];\n" - " $no_static = [];\n" + " $static = [];\n" + " $noStatic = [];\n" " foreach ($data as $k => $v) {\n" - " $name = $v->getName();\n" - " $line = $v->getStartLine();\n" + " $name = $v->getName();\n" + " $line = $v->getStartLine();\n" " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" - " 'name' => $name,\n" - " 'line' => $line ?: '',\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " } else {\n" - " $no_static[] = [\n" - " 'name' => $name,\n" - " 'line' => $line ?: '',\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " }\n" " }\n" - " return ['static' => $static, 'no_static' => $no_static];\n" + " return ['static' => $static, 'no_static' => $noStatic];\n" " };\n" "\n" " if ($tmpStaticMethods) {\n" " $tmpStaticMethods = $getTmpMethods($tmpStaticMethods);\n" - " $staticMethods = $tmpStaticMethods['static'];\n" - " $methods = $tmpStaticMethods['no_static'];\n" + " $staticMethods = $tmpStaticMethods['static'];\n" + " $methods = $tmpStaticMethods['no_static'];\n" " }\n" "\n" " $tmpParentClass = $class->getParentClass();\n" - " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" - "\n" - " $tmpInterface = $class->getInterfaceNames();\n" - " $interface = $tmpInterface ? $tmpInterface : [];\n" + " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" "\n" " $data = [\n" - " 'filename' => $filename,\n" - " 'constants' => $constants,\n" + " 'filename' => $filename,\n" + " 'constants' => $constants,\n" " 'staticProperties' => $staticProperties,\n" - " 'properties' => $properties,\n" - " 'staticMethods' => $staticMethods,\n" - " 'methods' => $methods,\n" - " 'parentClass' => $parentClass,\n" - " 'interface' => $interface,\n" + " 'properties' => $properties,\n" + " 'staticMethods' => $staticMethods,\n" + " 'methods' => $methods,\n" + " 'parentClass' => $parentClass,\n" + " 'interface' => $class->getInterfaceNames(),\n" " ];\n" " return self::json($data);\n" " }\n" "\n" - " public static function handlerGetFunctionInfo($server, $msg)\n" + " public static function handlerGetFunctionInfo(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" - " if (!$json || empty($json['function_name'])) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + "\n" + " $className = $json['class_name'] ?? '';\n" + " $functionName = $json['function_name'] ?? '';\n" + "\n" + " if (empty($json) || empty($functionName)) {\n" " return self::json('require function_name', 4004);\n" " }\n" - " if (!function_exists($json['function_name'])) {\n" - " return self::json(\"{$json['function_name']} not exists\", 4004);\n" + "\n" + " $isStatic = false;\n" + " if (!empty($className)) {\n" + " if (!class_exists($className) && !interface_exists($className)) {\n" + " return self::json(\"{$className} not exists\", 4004);\n" + " }\n" + " if (!method_exists($className, $functionName)) {\n" + " return self::json(\"{$className}->{$functionName} not exists\", 4004);\n" + " }\n" + " $ref = new \\ReflectionMethod($className, $functionName);\n" + " $isStatic = $ref->isStatic();\n" + " } else {\n" + " if (!function_exists($functionName)) {\n" + " return self::json(\"{$functionName} not exists\", 4004);\n" + " }\n" + " $ref = new \\ReflectionFunction($functionName);\n" " }\n" - " $function = new \\ReflectionFunction($json['function_name']);\n" "\n" " $result = [\n" - " 'filename' => $function->getFileName(),\n" - " 'line' => $function->getStartLine() ?? '',\n" - " 'num' => $function->getNumberOfParameters(),\n" - " 'user_defined' => $function->isUserDefined(),\n" - " 'extension' => $function->getExtensionName(),\n" + " 'filename' => $ref->getFileName(),\n" + " 'line' => $ref->getStartLine() ?: '',\n" + " 'num' => $ref->getNumberOfParameters(),\n" + " 'user_defined' => $ref->isUserDefined(),\n" + " 'extension' => $ref->getExtensionName(),\n" + " 'is_static' => $isStatic,\n" " ];\n" "\n" - " $params = $function->getParameters();\n" + " $params = $ref->getParameters();\n" "\n" " $list = [];\n" " foreach ($params as $param) {\n" " $type = $optional = $default = '';\n" "\n" + " $paramName = $param->getName();\n" + "\n" " if ($param->hasType()) {\n" - " /** @var \\ReflectionNamedType|\\ReflectionUnionType $getType */\n" - " $getType = $param->getType();\n" - " if ($getType instanceof \\ReflectionUnionType) {\n" + " /** @var \\ReflectionNamedType|\\ReflectionUnionType $reflection */\n" + " $reflection = $param->getType();\n" + " if ($reflection instanceof \\ReflectionUnionType) {\n" " $unionType = [];\n" - " foreach ($getType->getTypes() as $objType) {\n" + " foreach ($reflection->getTypes() as $objType) {\n" " $unionType[] = $objType->getName();\n" " }\n" " $type = implode('|', $unionType);\n" " } else {\n" - " $type = $getType->getName();\n" + " $type = $reflection->getName();\n" " }\n" " }\n" "\n" " if ($param->isOptional() && !$param->isVariadic()) {\n" - " if (!$result['user_defined'] && PHP_VERSION_ID < 80000) {\n" - " continue;\n" - " }\n" " $optional = '?';\n" " if ($param->isDefaultValueAvailable()) {\n" " $value = $param->getDefaultValue();\n" @@ -7577,26 +7932,30 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $isPassedByReference = $param->isPassedByReference() ? '&' : '';\n" - " $isVariadic = $param->isVariadic() ? '...' : '';\n" + " $isVariadic = $param->isVariadic() ? '...' : '';\n" "\n" " $option = \"{$optional}{$type} {$isPassedByReference}{$isVariadic}\";\n" - " $param = \"\\${$param->getName()}{$default}\";\n" - " $param_value = $option !== ' ' ? \"{$option}{$param}\" : $param;\n" - " $list[] = $param_value;\n" + " $param = \"\\${$paramName}{$default}\";\n" + "\n" + " $list[] = [\n" + " 'optional' => $optional,\n" + " 'type' => $type,\n" + " 'is_passed_by_reference' => $isPassedByReference,\n" + " 'is_variadic' => $isVariadic,\n" + " 'name' => $paramName,\n" + " 'default' => $default,\n" + " 'full' => $option !== ' ' ? \"{$option}{$param}\" : $param,\n" + " ];\n" " }\n" " $result['params'] = $list;\n" "\n" " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetObjectByHandle($server, $msg)\n" + " public static function handlerGetObjectByHandle(Server $server, string $msg)\n" " {\n" - " if (!function_exists('swoole_get_object_by_handle')) {\n" - " return self::json(['require ext-swoole_plus'], 5000);\n" - " }\n" - "\n" - " $json = json_decode($msg, true);\n" - " if (!$json || !isset($json['object_id']) || empty($json['object_id']) || !isset($json['object_hash']) || empty($json['object_hash'])) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json) || empty($json['object_id']) || empty($json['object_hash'])) {\n" " return self::json(['error' => 'Params Error!'], 4004);\n" " }\n" "\n" @@ -7605,7 +7964,7 @@ static const char* swoole_library_source_core_server_admin = " return self::json(['error' => 'Object destroyed!'], 4004);\n" " }\n" "\n" - " $object_hash = spl_object_hash($object);\n" + " $object_hash = spl_object_hash($object); // @phpstan-ignore argument.type\n" " if ($object_hash != $json['object_hash']) {\n" " return self::json(['error' => 'Object destroyed!'], 4004);\n" " }\n" @@ -7613,11 +7972,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json(var_export($object, true));\n" " }\n" "\n" - " public static function handlerGetVersionInfo($server, $msg)\n" + " public static function handlerGetVersionInfo(Server $server, string $msg)\n" " {\n" " $ip_arr = swoole_get_local_ip();\n" - " $host = [];\n" - " $local = [];\n" + " $host = [];\n" + " $local = [];\n" " foreach ($ip_arr as $k => $ip) {\n" " if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {\n" " $host[] = $ip;\n" @@ -7626,67 +7985,73 @@ static const char* swoole_library_source_core_server_admin = " }\n" " }\n" " $data = [\n" - " 'os' => php_uname('s') . '-' . php_uname('r'),\n" + " 'os' => php_uname('s') . '-' . php_uname('r'),\n" " 'swoole' => swoole_version(),\n" - " 'php' => phpversion(),\n" - " 'ip' => $host ? $host[0] : $local[0],\n" + " 'php' => phpversion(),\n" + " 'ip' => $host ? $host[0] : $local[0],\n" " ];\n" " return self::json($data);\n" " }\n" "\n" - " public static function handlerGetDefinedFunctions($server, $msg)\n" + " public static function handlerGetDefinedFunctions(Server $server, string $msg)\n" " {\n" " $functions = get_defined_functions();\n" - " $arr = [];\n" - " if ($functions) {\n" - " $arr['internal'] = $functions['internal'];\n" - "\n" + " $arr = [\n" + " 'internal' => $functions['internal'],\n" + " ];\n" + " if (!empty($functions['user'])) {\n" " foreach ($functions['user'] as $function_name) {\n" - " $function = new \\ReflectionFunction($function_name);\n" - " $filename = $function->getFileName();\n" - " $line = $function->getStartLine();\n" + " $function = new \\ReflectionFunction($function_name);\n" + " $filename = $function->getFileName();\n" + " $line = $function->getStartLine();\n" " $arr['user'][] = [\n" " 'function' => $function_name,\n" " 'filename' => $filename,\n" - " 'line' => $line,\n" + " 'line' => $line,\n" " ];\n" " }\n" " }\n" " return self::json($arr);\n" " }\n" "\n" - " public static function handlerGetDeclaredClasses($server, $msg)\n" + " public static function handlerGetDeclaredClasses(Server $server, string $msg)\n" " {\n" " $classes = get_declared_classes();\n" - " $arr = [];\n" + " $arr = [];\n" " if ($classes) {\n" " foreach ($classes as $classes_name) {\n" " $function = new \\ReflectionClass($classes_name);\n" " $filename = $function->getFileName();\n" - " $line = $function->getStartLine();\n" - " $arr[] = [\n" - " 'class' => $classes_name,\n" + " $line = $function->getStartLine();\n" + " $arr[] = [\n" + " 'class' => $classes_name,\n" " 'filename' => $filename ?: '',\n" - " 'line' => $line ?: '',\n" + " 'line' => $line ?: '',\n" " ];\n" " }\n" " }\n" " return self::json($arr);\n" " }\n" "\n" - " public static function handlerGetServerMemoryUsage($server, $msg)\n" + " public static function handlerGetServerMemoryUsage(Server $server, string $msg)\n" " {\n" " $total = 0;\n" "\n" - " $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\n" + " }\n" " $total += $result['master'];\n" "\n" - " $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\n" + " }\n" " $total += $result['manager'];\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" " for ($i = 0; $i < $n; $i++) {\n" - " $key = 'worker-' . $i;\n" + " $key = 'worker-' . $i;\n" " $result[$key] = self::getProcessMemoryRealUsage($server->getWorkerPid($i));\n" " $total += $result[$key];\n" " }\n" @@ -7697,38 +8062,46 @@ static const char* swoole_library_source_core_server_admin = " // TODO: Support other OS\n" " if (PHP_OS_FAMILY === 'Linux') {\n" " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" - " $result['memory_size'] = $match[1] * 1024;\n" + " $result['memory_size'] = intval($match[1]) * 1024;\n" " }\n" "\n" " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetServerCpuUsage($server, $msg)\n" + " public static function handlerGetServerCpuUsage(Server $server, string $msg)\n" " {\n" " $total = 0;\n" "\n" - " $result['master'] = self::getProcessCpuUsage($server->master_pid);\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessCpuUsage($server->master_pid);\n" + " }\n" + "\n" " $total += $result['master'][1] ?? 0;\n" "\n" - " $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\n" + " }\n" + "\n" " $total += $result['manager'][1] ?? 0;\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" " for ($i = 0; $i < $n; $i++) {\n" - " $key = 'worker-' . $i;\n" + " $key = 'worker-' . $i;\n" " $result[$key] = self::getProcessCpuUsage($server->getWorkerPid($i))[1] ?? 0;\n" " $total += $result[$key];\n" " }\n" "\n" - " $result['total'] = $total;\n" + " $result['total'] = $total;\n" " $result['cpu_num'] = swoole_cpu_num();\n" "\n" " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetStaticPropertyValue($server, $msg)\n" + " public static function handlerGetStaticPropertyValue(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['class_name'])) {\n" " return self::json(['error' => 'require class_name!'], 4004);\n" " }\n" @@ -7736,21 +8109,15 @@ static const char* swoole_library_source_core_server_admin = " return self::json(['error' => 'require property_name!'], 4004);\n" " }\n" "\n" - " $class_name = $json['class_name'];\n" - " $property_name = $json['property_name'];\n" - "\n" - " $refClass = new ReflectionClass($class_name);\n" - " if (!$refClass) {\n" - " return self::json(\"class[{$class_name}] not exists\", 4004);\n" - " }\n" + " $className = $json['class_name'];\n" + " $propertyName = $json['property_name'];\n" "\n" - " $property = $refClass->getProperty($property_name);\n" - " if (!$property) {\n" - " return self::json(\"property[{$property_name}] not exists\", 4004);\n" + " if (!class_exists($className)) {\n" + " return self::json(\"class[{$className}] not exists\", 4004);\n" " }\n" "\n" - " $property->setAccessible(true);\n" - " $value = $property->getValue($property_name);\n" + " $reflection = new \\ReflectionClass($className);\n" + " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" "\n" " $result = [\n" " 'value' => var_export($value, true),\n" @@ -7758,14 +8125,163 @@ static const char* swoole_library_source_core_server_admin = " return self::json($result);\n" " }\n" "\n" + " private static function handlerMulti(Server $server, array $list)\n" + " {\n" + " $return_list = [];\n" + " foreach ($list as $key => $content) {\n" + " $path_array = swoole_string($content['path'])->trim('/')->split('/');\n" + " $cmd = $path_array->get(1)->toString();\n" + "\n" + " if ($path_array->count() == 2) {\n" + " $process = swoole_string('master');\n" + " } else {\n" + " $process = $path_array->get(2);\n" + " }\n" + "\n" + " $data = [];\n" + " $url_query = parse_url($process->toString(), PHP_URL_QUERY) ?? [];\n" + " if (!empty($url_query)) {\n" + " parse_str($url_query, $data);\n" + " }\n" + " $data = array_merge($data, $content['post'] ?? []);\n" + "\n" + " if ($process->startsWith('master')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('manager')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('all') || $process->startsWith('specific')) {\n" + " if (!in_array($process->toString(), self::$allList) && !$process->startsWith('specific')) {\n" + " $return_list[$key] = json_decode('{}');\n" + " continue;\n" + " }\n" + "\n" + " $result = self::handlerGetAll($server, $process, $cmd, $data);\n" + "\n" + " $return_list[$key] = ['code' => 0, 'data' => $result];\n" + " continue;\n" + " } else {\n" + " $array = $process->split('-');\n" + "\n" + " if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\n" + " $return_list[$key] = json_decode('{}');\n" + " continue;\n" + " }\n" + "\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " }\n" + "\n" + " $return_list[$key] = $server->command($cmd, $process_id, intval($process_type), $data, true);\n" + " }\n" + "\n" + " return $return_list;\n" + " }\n" + "\n" + " private static function handlerGetAll(Server $server, StringObject $process, $cmd, $data, bool $json_decode = true)\n" + " {\n" + " if ($process->equals('all')) {\n" + " $result = self::handlerGetMaster($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetManager($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetAllWorker($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('all_reactor')) {\n" + " $result = self::handlerGetAllReactor($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->equals('all_worker') || $process->equals('all_event_worker')) {\n" + " $result = self::handlerGetAllWorker($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('all_task')) {\n" + " $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" + " } else {\n" + " // specific\n" + " $result = [];\n" + " if (!empty($data['workers']) && is_array($data['workers'])) {\n" + " foreach ($data['workers'] as $name) {\n" + " $process = swoole_string($name);\n" + " if ($process->startsWith('master')) {\n" + " $result += self::handlerGetMaster($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('manager')) {\n" + " $result += self::handlerGetManager($cmd, $data, $server, $json_decode);\n" + " } else {\n" + " $array = $process->split('-');\n" + " if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\n" + " $result[$name] = $json_decode ? json_decode('{}') : $json_decode;\n" + " } else {\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " $result[$name] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " return $result;\n" + " }\n" + "\n" + " private static function handlerGetMaster($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list['master'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MASTER, $data, $json_decode);\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetManager($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list['manager'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MANAGER, $data, $json_decode);\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllReactor($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list = [];\n" + " if ($server->mode === SWOOLE_BASE) {\n" + " return $list;\n" + " }\n" + " $process_type = SWOOLE_SERVER_COMMAND_REACTOR_THREAD;\n" + " if (empty($server->setting['reactor_num'])) {\n" + " $reactor_num = $server->setting['worker_num'];\n" + " } else {\n" + " $reactor_num = $server->setting['reactor_num'];\n" + " }\n" + " for ($process_id = 0; $process_id < $reactor_num; $process_id++) {\n" + " $list[\"reactor-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllWorker($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $process_type = SWOOLE_SERVER_COMMAND_EVENT_WORKER;\n" + " $worker_num = $server->setting['worker_num'];\n" + " $list = [];\n" + " for ($process_id = 0; $process_id < $worker_num; $process_id++) {\n" + " $list[\"worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllTaskWorker($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $process_type = SWOOLE_SERVER_COMMAND_TASK_WORKER;\n" + " $list = [];\n" + " if (empty($server->setting['task_worker_num'])) {\n" + " return $list;\n" + " }\n" + " $task_worker_num = $server->setting['task_worker_num'];\n" + " for ($process_id = 0; $process_id < $task_worker_num; $process_id++) {\n" + " $list[\"task_worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" " private static function getProcessCpuUsage($pid)\n" " {\n" " // TODO: Support other OS\n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/stat\")) {\n" " return [0];\n" " }\n" "\n" - " $statAll = file_get_contents('/proc/stat');\n" + " $statAll = file_get_contents('/proc/stat');\n" " $statProc = file_get_contents(\"/proc/{$pid}/stat\");\n" "\n" " $dataAll = preg_split(\"/[ \\t]+/\", $statAll, 6);\n" @@ -7794,16 +8310,16 @@ static const char* swoole_library_source_core_server_admin = " {\n" " $array = [];\n" " // TODO: Support other OS\n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/status\")) {\n" " return $array;\n" " }\n" - " $status = swoole_string(trim(file_get_contents('/proc/' . $pid . '/status')));\n" - " $lines = $status->split(\"\\n\");\n" + " $status = swoole_string(trim(file_get_contents(\"/proc/{$pid}/status\")));\n" + " $lines = $status->split(\"\\n\");\n" " foreach ($lines as $l) {\n" " if (empty($l)) {\n" " continue;\n" " }\n" - " [$k, $v] = swoole_string($l)->split(':');\n" + " [$k, $v] = swoole_string($l)->split(':');\n" " $array[$k] = trim($v);\n" " }\n" " return $array;\n" @@ -7852,6 +8368,24 @@ static const char* swoole_library_source_core_server_admin = " return $size;\n" " }\n" "\n" + " private static function haveMasterProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" + " private static function haveManagerProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE && $server->getManagerPid() === 0) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" " private static function json($data, $code = 0)\n" " {\n" " $result = json_encode(['code' => $code, 'data' => $data], JSON_INVALID_UTF8_IGNORE);\n" @@ -7879,191 +8413,240 @@ static const char* swoole_library_source_core_server_helper = "\n" "namespace Swoole\\Server;\n" "\n" + "use Swoole\\Constant;\n" + "use Swoole\\Coroutine;\n" "use Swoole\\Server;\n" "use Swoole\\Timer;\n" - "use function Swoole\\Coroutine\\go;\n" "\n" "class Helper\n" "{\n" " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" "\n" " public const GLOBAL_OPTIONS = [\n" - " 'debug_mode' => true,\n" - " 'trace_flags' => true,\n" - " 'log_file' => true,\n" - " 'log_level' => true,\n" - " 'log_date_format' => true,\n" - " 'log_date_with_microseconds' => true,\n" - " 'log_rotation' => true,\n" - " 'display_errors' => true,\n" - " 'dns_server' => true,\n" - " 'socket_dns_timeout' => true,\n" - " 'socket_connect_timeout' => true,\n" - " 'socket_write_timeout' => true,\n" - " 'socket_send_timeout' => true,\n" - " 'socket_read_timeout' => true,\n" - " 'socket_recv_timeout' => true,\n" - " 'socket_buffer_size' => true,\n" - " 'socket_timeout' => true,\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" + " 'http2_max_concurrent_streams' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" - " 'chroot' => true,\n" - " 'user' => true,\n" - " 'group' => true,\n" - " 'daemonize' => true,\n" - " 'pid_file' => true,\n" - " 'reactor_num' => true,\n" - " 'single_thread' => true,\n" - " 'worker_num' => true,\n" - " 'max_wait_time' => true,\n" - " 'max_queued_bytes' => true,\n" - " 'enable_coroutine' => true,\n" - " 'max_coro_num' => true,\n" - " 'max_coroutine' => true,\n" - " 'hook_flags' => true,\n" - " 'send_timeout' => true,\n" - " 'dispatch_mode' => true,\n" - " 'send_yield' => true,\n" - " 'dispatch_func' => true,\n" - " 'discard_timeout_request' => true,\n" - " 'enable_unsafe_event' => true,\n" - " 'enable_delay_receive' => true,\n" - " 'enable_reuse_port' => true,\n" - " 'task_use_object' => true,\n" - " 'task_object' => true,\n" - " 'event_object' => true,\n" - " 'task_enable_coroutine' => true,\n" - " 'task_worker_num' => true,\n" - " 'task_ipc_mode' => true,\n" - " 'task_tmpdir' => true,\n" - " 'task_max_request' => true,\n" - " 'task_max_request_grace' => true,\n" - " 'max_connection' => true,\n" - " 'max_conn' => true,\n" - " 'start_session_id' => true,\n" - " 'heartbeat_check_interval' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'max_request' => true,\n" - " 'max_request_grace' => true,\n" - " 'reload_async' => true,\n" - " 'open_cpu_affinity' => true,\n" - " 'cpu_affinity_ignore' => true,\n" - " 'http_parse_cookie' => true,\n" - " 'http_parse_post' => true,\n" - " 'http_parse_files' => true,\n" - " 'http_compression' => true,\n" - " 'http_compression_level' => true,\n" - " 'compression_min_length' => true,\n" - " 'http_gzip_level' => true,\n" - " 'websocket_compression' => true,\n" - " 'upload_tmp_dir' => true,\n" - " 'enable_static_handler' => true,\n" - " 'document_root' => true,\n" - " 'http_autoindex' => true,\n" - " 'http_index_files' => true,\n" - " 'static_handler_locations' => true,\n" - " 'input_buffer_size' => true,\n" - " 'buffer_input_size' => true,\n" - " 'output_buffer_size' => true,\n" - " 'buffer_output_size' => true,\n" - " 'message_queue_key' => true,\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" + " 'http_compression_min_length' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" + " 'init_arguments' => true,\n" + " 'bootstrap' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" - " 'ssl_cert_file' => true,\n" - " 'ssl_key_file' => true,\n" - " 'backlog' => true,\n" - " 'socket_buffer_size' => true,\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" " 'kernel_socket_recv_buffer_size' => true,\n" " 'kernel_socket_send_buffer_size' => true,\n" - " 'buffer_high_watermark' => true,\n" - " 'buffer_low_watermark' => true,\n" - " 'open_tcp_nodelay' => true,\n" - " 'tcp_defer_accept' => true,\n" - " 'open_tcp_keepalive' => true,\n" - " 'open_eof_check' => true,\n" - " 'open_eof_split' => true,\n" - " 'package_eof' => true,\n" - " 'open_http_protocol' => true,\n" - " 'open_websocket_protocol' => true,\n" - " 'websocket_subprotocol' => true,\n" - " 'open_websocket_close_frame' => true,\n" - " 'open_websocket_ping_frame' => true,\n" - " 'open_websocket_pong_frame' => true,\n" - " 'open_http2_protocol' => true,\n" - " 'open_mqtt_protocol' => true,\n" - " 'open_redis_protocol' => true,\n" - " 'max_idle_time' => true,\n" - " 'tcp_keepidle' => true,\n" - " 'tcp_keepinterval' => true,\n" - " 'tcp_keepcount' => true,\n" - " 'tcp_user_timeout' => true,\n" - " 'tcp_fastopen' => true,\n" - " 'open_length_check' => true,\n" - " 'package_length_type' => true,\n" - " 'package_length_offset' => true,\n" - " 'package_body_offset' => true,\n" - " 'package_body_start' => true,\n" - " 'package_length_func' => true,\n" - " 'package_max_length' => true,\n" - " 'ssl_compress' => true,\n" - " 'ssl_protocols' => true,\n" - " 'ssl_verify_peer' => true,\n" - " 'ssl_allow_self_signed' => true,\n" - " 'ssl_client_cert_file' => true,\n" - " 'ssl_verify_depth' => true,\n" - " 'ssl_prefer_server_ciphers' => true,\n" - " 'ssl_ciphers' => true,\n" - " 'ssl_ecdh_curve' => true,\n" - " 'ssl_dhparam' => true,\n" - " 'ssl_sni_certs' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" + " ];\n" + "\n" + " public const AIO_OPTIONS = [\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" + " 'dns_cache_refresh_time' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" + " 'iouring_entries' => true,\n" + " 'iouring_workers' => true,\n" + " 'iouring_flag' => true,\n" + " ];\n" + "\n" + " public const COROUTINE_OPTIONS = [\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" + " 'enable_preemptive_scheduler' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" + " 'max_concurrency' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" - " 'stats_file' => true,\n" + " 'stats_file' => true,\n" " 'stats_timer_interval' => true,\n" - " 'admin_server' => true,\n" + " 'admin_server' => true,\n" " ];\n" "\n" - " public static function checkOptions(array $input_options)\n" + " public static function checkOptions(array $input_options): void\n" " {\n" - " $const_options = self::GLOBAL_OPTIONS + self::SERVER_OPTIONS + self::PORT_OPTIONS + self::HELPER_OPTIONS;\n" + " $const_options = self::GLOBAL_OPTIONS + self::SERVER_OPTIONS + self::PORT_OPTIONS\n" + " + self::AIO_OPTIONS + self::COROUTINE_OPTIONS + self::HELPER_OPTIONS;\n" "\n" " foreach ($input_options as $k => $v) {\n" " if (!array_key_exists(strtolower($k), $const_options)) {\n" - " //TODO throw exception\n" + " // TODO throw exception\n" " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n" " }\n" " }\n" " }\n" "\n" - " public static function onBeforeStart(Server $server)\n" + " public static function onBeforeStart(Server $server): void\n" " {\n" " if (!empty($server->setting['admin_server'])) {\n" " Admin::init($server);\n" " }\n" " }\n" "\n" - " public static function onBeforeShutdown(Server $server)\n" + " public static function onBeforeShutdown(Server $server): void\n" " {\n" - " if ($server->admin_server) {\n" + " if (isset($server->admin_server)) { // @phpstan-ignore isset.property\n" " $server->admin_server->shutdown();\n" - " $server->admin_server = null;\n" + " $server->admin_server = null; // @phpstan-ignore assign.propertyType\n" " }\n" " }\n" "\n" - " public static function onWorkerStart(Server $server, int $workerId)\n" + " public static function onWorkerStart(Server $server, int $workerId): void\n" " {\n" " if (!empty($server->setting['stats_file']) and $workerId == 0) {\n" " $interval_ms = empty($server->setting['stats_timer_interval']) ? self::STATS_TIMER_INTERVAL_TIME : intval($server->setting['stats_timer_interval']);\n" "\n" " $server->stats_timer = Timer::tick($interval_ms, function () use ($server) {\n" - " $stats = $server->stats();\n" + " $stats = $server->stats();\n" " $stats_file = swoole_string($server->setting['stats_file']);\n" " if ($stats_file->endsWith('.json')) {\n" - " $out = json_encode($stats);\n" + " $out = json_encode($stats, JSON_THROW_ON_ERROR);\n" " } elseif ($stats_file->endsWith('.php')) {\n" " $out = \"stats_timer) {\n" " Timer::clear($server->stats_timer);\n" @@ -8090,10 +8673,10 @@ static const char* swoole_library_source_core_server_helper = " {\n" " }\n" "\n" - " public static function onStart(Server $server)\n" + " public static function onStart(Server $server): void\n" " {\n" - " if (!empty($server->setting['admin_server'])) {\n" - " go(function () use ($server) {\n" + " if (!empty($server->setting[Constant::OPTION_ADMIN_SERVER])) {\n" + " Coroutine::create(function () use ($server): void {\n" " Admin::start($server);\n" " });\n" " }\n" @@ -8124,7 +8707,7 @@ static const char* swoole_library_source_core_server_helper = " }\n" "}\n"; -static const char* swoole_library_source_core_coroutine_functions = +static const char* swoole_library_source_core_name_resolver = "\n" "/**\n" " * This file is part of Swoole.\n" @@ -8136,65 +8719,788 @@ static const char* swoole_library_source_core_coroutine_functions = "\n" "declare(strict_types=1);\n" "\n" - "namespace Swoole\\Coroutine;\n" + "namespace Swoole;\n" "\n" - "use Swoole\\Coroutine;\n" + "use Swoole\\Coroutine\\Http\\ClientProxy;\n" + "use Swoole\\Http\\Status;\n" + "use Swoole\\NameResolver\\Cluster;\n" + "use Swoole\\NameResolver\\Exception;\n" "\n" - "function run(callable $fn, ...$args)\n" + "abstract class NameResolver\n" "{\n" - " $s = new Scheduler();\n" - " $options = Coroutine::getOptions();\n" - " if (!isset($options['hook_flags'])) {\n" - " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" + " protected $baseUrl;\n" + "\n" + " protected $info;\n" + "\n" + " private $filter_fn;\n" + "\n" + " public function __construct($url, protected $prefix = 'swoole_service_')\n" + " {\n" + " $this->checkServerUrl($url);\n" " }\n" - " $s->add($fn, ...$args);\n" - " return $s->start();\n" - "}\n" "\n" - "function go(callable $fn, ...$args)\n" - "{\n" - " return Coroutine::create($fn, ...$args);\n" - "}\n" + " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" "\n" - "function defer(callable $fn)\n" - "{\n" - " Coroutine::defer($fn);\n" - "}\n" + " abstract public function leave(string $name, string $ip, int $port): bool;\n" "\n" - "function batch(array $tasks, float $timeout = -1): array\n" - "{\n" - " $wg = new WaitGroup(count($tasks));\n" - " foreach ($tasks as $id => $task) {\n" - " Coroutine::create(function () use ($wg, &$tasks, $id, $task) {\n" - " $tasks[$id] = null;\n" - " $tasks[$id] = $task();\n" - " $wg->done();\n" - " });\n" + " abstract public function getCluster(string $name): ?Cluster;\n" + "\n" + " public function withFilter(callable $fn): self\n" + " {\n" + " $this->filter_fn = $fn;\n" + " return $this;\n" " }\n" - " $wg->wait($timeout);\n" - " return $tasks;\n" - "}\n" "\n" - "function parallel(int $n, callable $fn): void\n" - "{\n" - " $count = $n;\n" - " $wg = new WaitGroup($n);\n" - " while ($count--) {\n" - " Coroutine::create(function () use ($fn, $wg) {\n" - " $fn();\n" - " $wg->done();\n" - " });\n" + " public function getFilter()\n" + " {\n" + " return $this->filter_fn;\n" " }\n" - " $wg->wait();\n" - "}\n" "\n" - "function map(array $list, callable $fn, float $timeout = -1): array\n" - "{\n" - " $wg = new WaitGroup(count($list));\n" - " foreach ($list as $id => $elem) {\n" - " Coroutine::create(function () use ($wg, &$list, $id, $elem, $fn): void {\n" + " public function hasFilter(): bool\n" + " {\n" + " return !empty($this->filter_fn);\n" + " }\n" + "\n" + " /**\n" + " * return string: final result, non-empty string must be a valid IP address,\n" + " * and an empty string indicates name lookup failed, and lookup operation will not continue.\n" + " * return Cluster: has multiple nodes and failover is possible\n" + " * return false or null: try another name resolver\n" + " * @return Cluster|false|string|null\n" + " */\n" + " public function lookup(string $name)\n" + " {\n" + " if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\n" + " return null;\n" + " }\n" + " $cluster = $this->getCluster($name);\n" + " // lookup failed, terminate execution\n" + " if ($cluster == null) {\n" + " return '';\n" + " }\n" + " // only one node, cannot retry\n" + " if ($cluster->count() == 1) {\n" + " return $cluster->pop();\n" + " }\n" + " return $cluster;\n" + " }\n" + "\n" + " /**\n" + " * !!! The host MUST BE IP ADDRESS\n" + " */\n" + " protected function checkServerUrl(string $url)\n" + " {\n" + " $info = parse_url($url);\n" + " if (empty($info['scheme']) or empty($info['host'])) {\n" + " throw new \\RuntimeException(\"invalid url parameter '{$url}'\");\n" + " }\n" + " if (!filter_var($info['host'], FILTER_VALIDATE_IP)) {\n" + " $info['ip'] = gethostbyname($info['host']);\n" + " if (!filter_var($info['ip'], FILTER_VALIDATE_IP)) {\n" + " throw new \\RuntimeException(\"Failed to resolve host '{$info['host']}'\");\n" + " }\n" + " } else {\n" + " $info['ip'] = $info['host'];\n" + " }\n" + " $baseUrl = $info['scheme'] . '://' . $info['ip'];\n" + " if (!empty($info['port'])) {\n" + " $baseUrl .= \":{$info['port']}\";\n" + " }\n" + " if (!empty($info['path'])) {\n" + " $baseUrl .= rtrim($info['path'], '/');\n" + " }\n" + " $this->baseUrl = $baseUrl;\n" + " $this->info = $info;\n" + " }\n" + "\n" + " protected function checkResponse(ClientProxy $response): bool\n" + " {\n" + " if ($response->getStatusCode() === Status::OK) {\n" + " return true;\n" + " }\n" + "\n" + " throw new Exception('Http Body: ' . $response->getBody(), $response->getStatusCode());\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_exception = + "\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" + "declare(strict_types=1);\n" + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "class Exception extends \\RuntimeException\n" + "{\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_cluster = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\Exception;\n" + "\n" + "class Cluster\n" + "{\n" + " private array $nodes = [];\n" + "\n" + " /**\n" + " * @throws Exception\n" + " */\n" + " public function add(string $host, int $port, int $weight = 100): void\n" + " {\n" + " if (!filter_var($host, FILTER_VALIDATE_IP)) {\n" + " throw new Exception(\"Bad IP Address [{$host}]\");\n" + " }\n" + " if ($port < 0 or $port > 65535) {\n" + " throw new Exception(\"Bad Port [{$port}]\");\n" + " }\n" + " if ($weight < 0 or $weight > 100) {\n" + " throw new Exception(\"Bad Weight [{$weight}]\");\n" + " }\n" + " $this->nodes[] = ['host' => $host, 'port' => $port, 'weight' => $weight];\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public function pop()\n" + " {\n" + " if (empty($this->nodes)) {\n" + " return false;\n" + " }\n" + " $index = array_rand($this->nodes, 1);\n" + " $node = $this->nodes[$index];\n" + " unset($this->nodes[$index]);\n" + " return $node;\n" + " }\n" + "\n" + " public function count(): int\n" + " {\n" + " return count($this->nodes);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_redis = + "\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" + "declare(strict_types=1);\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\NameResolver;\n" + "\n" + "class Redis extends NameResolver\n" + "{\n" + " private $serverHost;\n" + "\n" + " private $serverPort;\n" + "\n" + " public function __construct($url, $prefix = 'swoole:service:')\n" + " {\n" + " parent::__construct($url, $prefix);\n" + " $this->serverHost = $this->info['ip'];\n" + " $this->serverPort = $this->info['port'] ?? 6379;\n" + " }\n" + "\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return false;\n" + " }\n" + " if ($redis->sAdd($this->prefix . $name, $ip . ':' . $port) === false) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return false;\n" + " }\n" + " if ($redis->sRem($this->prefix . $name, $ip . ':' . $port) === false) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return null;\n" + " }\n" + " $members = $redis->sMembers($this->prefix . $name);\n" + " if (empty($members)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($members as $m) {\n" + " [$host, $port] = explode(':', $m);\n" + " $cluster->add($host, intval($port));\n" + " }\n" + " return $cluster;\n" + " }\n" + "\n" + " protected function connect()\n" + " {\n" + " $redis = new \\Redis();\n" + " if ($redis->connect($this->serverHost, $this->serverPort) === false) {\n" + " return false;\n" + " }\n" + " return $redis;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_nacos = + "\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" + "declare(strict_types=1);\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\Coroutine;\n" + "use Swoole\\NameResolver;\n" + "\n" + "class Nacos extends NameResolver\n" + "{\n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception\n" + " */\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" + " $params['healthy'] = 'true';\n" + " $params['weight'] = $options['weight'] ?? 100;\n" + " $params['encoding'] = $options['encoding'] ?? 'utf-8';\n" + " $params['namespaceId'] = $options['namespaceId'] ?? 'public';\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" + " $r = Coroutine\\Http\\post($url, []);\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception\n" + " */\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" + " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception|\\Swoole\\Exception\n" + " */\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params);\n" + " $r = Coroutine\\Http\\get($url);\n" + " if (!$this->checkResponse($r)) {\n" + " return null;\n" + " }\n" + " $result = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($result)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($result->hosts as $node) {\n" + " $cluster->add($node->ip, $node->port, $node->weight);\n" + " }\n" + " return $cluster;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_consul = + "\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" + "declare(strict_types=1);\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\NameResolver;\n" + "\n" + "use function Swoole\\Coroutine\\Http\\get;\n" + "use function Swoole\\Coroutine\\Http\\request;\n" + "\n" + "class Consul extends NameResolver\n" + "{\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " $weight = $options['weight'] ?? 100;\n" + " $data = [\n" + " 'ID' => $this->getServiceId($name, $ip, $port),\n" + " 'Name' => $this->prefix . $name,\n" + " 'Address' => $ip,\n" + " 'Port' => $port,\n" + " 'EnableTagOverride' => false,\n" + " 'Weights' => [\n" + " 'Passing' => $weight,\n" + " 'Warning' => 1,\n" + " ],\n" + " ];\n" + " $url = $this->baseUrl . '/v1/agent/service/register';\n" + " $r = request($url, 'PUT', json_encode($data, JSON_THROW_ON_ERROR));\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " $url = $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " );\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " public function enableMaintenanceMode(string $name, string $ip, int $port): bool\n" + " {\n" + " $url = $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " );\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\n" + " $r = get($url);\n" + " if (!$this->checkResponse($r)) {\n" + " return null;\n" + " }\n" + " $list = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($list)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($list as $li) {\n" + " $cluster->add($li->ServiceAddress, $li->ServicePort, $li->ServiceWeights->Passing);\n" + " }\n" + " return $cluster;\n" + " }\n" + "\n" + " private function getServiceId(string $name, string $ip, int $port): string\n" + " {\n" + " return $this->prefix . $name . \"_{$ip}:{$port}\";\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_thread_pool = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "use PhpParser\\Error;\n" + "use PhpParser\\ParserFactory;\n" + "use Swoole\\Thread;\n" + "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" + "class Pool\n" + "{\n" + " private array $threads = [];\n" + "\n" + " private string $autoloader = '';\n" + "\n" + " private string $classDefinitionFile = '';\n" + "\n" + " private string $runnableClass = '';\n" + "\n" + " private int $threadNum = 0;\n" + "\n" + " private string $proxyFile;\n" + "\n" + " private array $arguments = [];\n" + "\n" + " private object $running;\n" + "\n" + " private object $queue;\n" + "\n" + " private array $indexes = [];\n" + "\n" + " public function __construct(string $runnableClass, int $threadNum)\n" + " {\n" + " if ($threadNum <= 0) {\n" + " throw new \\Exception('threadNum must be greater than 0');\n" + " }\n" + " $this->runnableClass = $runnableClass;\n" + " $this->threadNum = $threadNum;\n" + " }\n" + "\n" + " public function withArguments(...$arguments): static\n" + " {\n" + " $this->arguments = $arguments;\n" + " return $this;\n" + " }\n" + "\n" + " public function withAutoloader(string $autoloader): static\n" + " {\n" + " $this->autoloader = $autoloader;\n" + " return $this;\n" + " }\n" + "\n" + " public function withClassDefinitionFile(string $classDefinitionFile): static\n" + " {\n" + " $this->classDefinitionFile = $classDefinitionFile;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @throws \\ReflectionException\n" + " */\n" + " public function start(): void\n" + " {\n" + " if (empty($this->classDefinitionFile) and class_exists($this->runnableClass, false)) {\n" + " $file = (new \\ReflectionClass($this->runnableClass))->getFileName();\n" + " if (!$this->isValidPhpFile($file)) {\n" + " throw new \\Exception('class definition file must not contain any expressions.');\n" + " }\n" + " $this->classDefinitionFile = $file;\n" + " } elseif ($this->classDefinitionFile) {\n" + " require_once $this->classDefinitionFile;\n" + " }\n" + "\n" + " if (!class_exists($this->runnableClass)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` not found\");\n" + " }\n" + "\n" + " if (!is_subclass_of($this->runnableClass, Runnable::class)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` must implements Thread\\\\Runnable\");\n" + " }\n" + "\n" + " if (empty($this->autoloader)) {\n" + " $include_files = get_included_files();\n" + " foreach ($include_files as $file) {\n" + " if (str_ends_with($file, 'vendor/autoload.php')) {\n" + " $this->autoloader = $file;\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "\n" + " if ($this->autoloader) {\n" + " $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\n" + " } else {\n" + " $this->proxyFile = dirname($this->classDefinitionFile) . '/thread_runner.php';\n" + " }\n" + "\n" + " if (!is_file($this->proxyFile)) {\n" + " $script = 'run($threadArguments); }' . PHP_EOL;\n" + " $script .= 'finally { $queue->push($threadId, Swoole\\Thread\\Queue::NOTIFY_ONE); }' . PHP_EOL;\n" + " $script .= PHP_EOL;\n" + " file_put_contents($this->proxyFile, $script);\n" + " }\n" + "\n" + " $this->queue = new Queue();\n" + " $this->running = new Atomic(1);\n" + "\n" + " for ($index = 0; $index < $this->threadNum; $index++) {\n" + " $this->createThread($index);\n" + " }\n" + "\n" + " while ($this->running->get()) {\n" + " $threadId = $this->queue->pop(-1);\n" + " $thread = $this->threads[$threadId];\n" + " $index = $this->indexes[$threadId];\n" + " $thread->join();\n" + " unset($this->threads[$threadId], $this->indexes[$threadId]);\n" + "\n" + " $this->createThread($index);\n" + " }\n" + "\n" + " foreach ($this->threads as $thread) {\n" + " $thread->join();\n" + " }\n" + " }\n" + "\n" + " public function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "\n" + " protected function isValidPhpFile($filePath): bool\n" + " {\n" + " $allowedNodeTypes = [\n" + " \\PhpParser\\Node\\Stmt\\Class_::class,\n" + " \\PhpParser\\Node\\Stmt\\Const_::class,\n" + " \\PhpParser\\Node\\Stmt\\Use_::class,\n" + " \\PhpParser\\Node\\Stmt\\Namespace_::class,\n" + " \\PhpParser\\Node\\Stmt\\Declare_::class,\n" + " ];\n" + "\n" + " $parser = (new ParserFactory())->createForNewestSupportedVersion();\n" + " try {\n" + " $code = file_get_contents($filePath);\n" + " $stmts = $parser->parse($code);\n" + " $skipLine = -1;\n" + " foreach ($stmts as $stmt) {\n" + " $isAllowed = false;\n" + " foreach ($allowedNodeTypes as $allowedNodeType) {\n" + " if ($stmt instanceof $allowedNodeType) {\n" + " $isAllowed = true;\n" + " break;\n" + " }\n" + " }\n" + " if (!$isAllowed) {\n" + " if ($stmt->getLine() == $skipLine) {\n" + " continue;\n" + " }\n" + " return false;\n" + " }\n" + " }\n" + " } catch (Error $error) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " protected function createThread($index): void\n" + " {\n" + " $thread = new Thread($this->proxyFile,\n" + " $this->autoloader,\n" + " $this->runnableClass,\n" + " $this->queue,\n" + " $this->classDefinitionFile,\n" + " $this->running,\n" + " $index,\n" + " ...$this->arguments\n" + " );\n" + " $this->indexes[$thread->id] = $index;\n" + " $this->threads[$thread->id] = $thread;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_thread_runnable = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" + "abstract class Runnable\n" + "{\n" + " protected Atomic $running;\n" + "\n" + " protected int $id;\n" + "\n" + " public function __construct($running, $index)\n" + " {\n" + " $this->running = $running;\n" + " $this->id = $index;\n" + " }\n" + "\n" + " abstract public function run(array $args): void;\n" + "\n" + " protected function isRunning(): bool\n" + " {\n" + " return $this->running->get() === 1;\n" + " }\n" + "\n" + " protected function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_functions = + "\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" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Coroutine;\n" + "\n" + "use Swoole\\Coroutine;\n" + "\n" + "function run(callable $fn, ...$args)\n" + "{\n" + " $s = new Scheduler();\n" + " $options = Coroutine::getOptions();\n" + " if (!isset($options['hook_flags'])) {\n" + " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" + " }\n" + " $s->add($fn, ...$args);\n" + " return $s->start();\n" + "}\n" + "\n" + "function go(callable $fn, ...$args)\n" + "{\n" + " return Coroutine::create($fn, ...$args);\n" + "}\n" + "\n" + "function defer(callable $fn)\n" + "{\n" + " Coroutine::defer($fn);\n" + "}\n" + "\n" + "function batch(array $tasks, float $timeout = -1): array\n" + "{\n" + " $wg = new WaitGroup(count($tasks));\n" + " foreach ($tasks as $id => $task) {\n" + " Coroutine::create(function () use ($wg, &$tasks, $id, $task) {\n" + " $tasks[$id] = null;\n" + " $tasks[$id] = $task();\n" + " $wg->done();\n" + " });\n" + " }\n" + " $wg->wait($timeout);\n" + " return $tasks;\n" + "}\n" + "\n" + "function parallel(int $n, callable $fn): void\n" + "{\n" + " $count = $n;\n" + " $wg = new WaitGroup($n);\n" + " while ($count--) {\n" + " Coroutine::create(function () use ($fn, $wg) {\n" + " $fn();\n" + " $wg->done();\n" + " });\n" + " }\n" + " $wg->wait();\n" + "}\n" + "\n" + "/**\n" + " * Applies the callback to the elements of the given list.\n" + " *\n" + " * The callback function takes on two parameters. The list parameter's value being the first, and the key/index second.\n" + " * Each callback runs in a new coroutine, allowing the list to be processed in parallel.\n" + " *\n" + " * @param array $list A list of key/value paired input data.\n" + " * @param callable $fn The callback function to apply to each item on the list. The callback takes on two parameters.\n" + " * The list parameter's value being the first, and the key/index second.\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return array Returns an array containing the results of applying the callback function to the corresponding value\n" + " * and key of the list (used as arguments for the callback). The returned array will preserve the keys of\n" + " * the list.\n" + " */\n" + "function map(array $list, callable $fn, float $timeout = -1): array\n" + "{\n" + " $wg = new WaitGroup(count($list));\n" + " foreach ($list as $id => $elem) {\n" + " Coroutine::create(function () use ($wg, &$list, $id, $elem, $fn): void {\n" " $list[$id] = null;\n" - " $list[$id] = $fn($elem);\n" + " $list[$id] = $fn($elem, $id);\n" " $wg->done();\n" " });\n" " }\n" @@ -8205,7 +9511,7 @@ static const char* swoole_library_source_core_coroutine_functions = "function deadlock_check()\n" "{\n" " $all_coroutines = Coroutine::listCoroutines();\n" - " $count = Coroutine::stats()['coroutine_num'];\n" + " $count = Coroutine::stats()['coroutine_num'];\n" " echo \"\\n===================================================================\",\n" " \"\\n [FATAL ERROR]: all coroutines (count: {$count}) are asleep - deadlock!\",\n" " \"\\n===================================================================\\n\";\n" @@ -8221,7 +9527,7 @@ static const char* swoole_library_source_core_coroutine_functions = " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" " echo \"\\n\";\n" " $index++;\n" - " //limit the number of maximum outputs\n" + " // limit the number of maximum outputs\n" " if ($index >= $limit) {\n" " break;\n" " }\n" @@ -8272,46 +9578,32 @@ static const char* swoole_library_source_ext_curl = "{\n" " $info = $obj->getInfo();\n" " if (is_array($info) and $opt) {\n" - " switch ($opt) {\n" - " case CURLINFO_EFFECTIVE_URL:\n" - " return $info['url'];\n" - " case CURLINFO_HTTP_CODE:\n" - " return $info['http_code'];\n" - " case CURLINFO_CONTENT_TYPE:\n" - " return $info['content_type'];\n" - " case CURLINFO_REDIRECT_COUNT:\n" - " return $info['redirect_count'];\n" - " case CURLINFO_REDIRECT_URL:\n" - " return $info['redirect_url'];\n" - " case CURLINFO_TOTAL_TIME:\n" - " return $info['total_time'];\n" - " case CURLINFO_STARTTRANSFER_TIME:\n" - " return $info['starttransfer_time'];\n" - " case CURLINFO_SIZE_DOWNLOAD:\n" - " return $info['size_download'];\n" - " case CURLINFO_SPEED_DOWNLOAD:\n" - " return $info['speed_download'];\n" - " case CURLINFO_REDIRECT_TIME:\n" - " return $info['redirect_time'];\n" - " case CURLINFO_HEADER_SIZE:\n" - " return $info['header_size'];\n" - " case CURLINFO_PRIMARY_IP:\n" - " return $info['primary_ip'];\n" - " case CURLINFO_PRIVATE:\n" - " return $info['private'];\n" - " default:\n" - " return null;\n" - " }\n" + " return match ($opt) {\n" + " CURLINFO_EFFECTIVE_URL => $info['url'],\n" + " CURLINFO_HTTP_CODE => $info['http_code'],\n" + " CURLINFO_CONTENT_TYPE => $info['content_type'],\n" + " CURLINFO_REDIRECT_COUNT => $info['redirect_count'],\n" + " CURLINFO_REDIRECT_URL => $info['redirect_url'],\n" + " CURLINFO_TOTAL_TIME => $info['total_time'],\n" + " CURLINFO_STARTTRANSFER_TIME => $info['starttransfer_time'],\n" + " CURLINFO_SIZE_DOWNLOAD => $info['size_download'],\n" + " CURLINFO_SPEED_DOWNLOAD => $info['speed_download'],\n" + " CURLINFO_REDIRECT_TIME => $info['redirect_time'],\n" + " CURLINFO_HEADER_SIZE => $info['header_size'],\n" + " CURLINFO_PRIMARY_IP => $info['primary_ip'],\n" + " CURLINFO_PRIVATE => $info['private'],\n" + " default => null,\n" + " };\n" " }\n" " return $info;\n" "}\n" "\n" - "function swoole_curl_errno(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_errno(Swoole\\Curl\\Handler $obj): int\n" "{\n" " return $obj->errno();\n" "}\n" "\n" - "function swoole_curl_error(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_error(Swoole\\Curl\\Handler $obj): string\n" "{\n" " return $obj->error();\n" "}\n" @@ -8321,9 +9613,9 @@ static const char* swoole_library_source_ext_curl = " return $obj->reset();\n" "}\n" "\n" - "function swoole_curl_close(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_close(Swoole\\Curl\\Handler $obj): void\n" "{\n" - " return $obj->close();\n" + " $obj->close();\n" "}\n" "\n" "function swoole_curl_multi_getcontent(Swoole\\Curl\\Handler $obj)\n" @@ -8363,7 +9655,7 @@ static const char* swoole_library_source_ext_sockets = " return $socket->recv($length);\n" "}\n" "\n" - "function swoole_socket_write(Socket $socket, string $buffer, int $length = 0): int\n" + "function swoole_socket_write(Socket $socket, string $buffer, int $length = 0)\n" "{\n" " if ($length > 0 and $length < strlen($buffer)) {\n" " $buffer = substr($buffer, 0, $length);\n" @@ -8371,7 +9663,7 @@ static const char* swoole_library_source_ext_sockets = " return $socket->send($buffer);\n" "}\n" "\n" - "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags): int\n" + "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" @@ -8382,7 +9674,7 @@ static const char* swoole_library_source_ext_sockets = "function swoole_socket_recv(Socket $socket, &$buffer, int $length, int $flags)\n" "{\n" " if ($flags & MSG_OOB) {\n" - " throw new RuntimeException('$flags[MSG_OOB] is not supported');\n" + " throw new RuntimeException('\\$flags[MSG_OOB] is not supported');\n" " }\n" " if ($flags & MSG_PEEK) {\n" " $buffer = $socket->peek($length);\n" @@ -8413,11 +9705,15 @@ static const char* swoole_library_source_ext_sockets = " return $socket->sendto($addr, $port, $buffer);\n" "}\n" "\n" - "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port)\n" + "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port = null)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" " }\n" + " if ($length == 0) {\n" + " $socket->errCode = SOCKET_EAGAIN;\n" + " return false;\n" + " }\n" " if ($socket->type != SOCK_DGRAM) {\n" " throw new RuntimeException('only supports dgram type socket');\n" " }\n" @@ -8434,7 +9730,7 @@ static const char* swoole_library_source_ext_sockets = " } else {\n" " $buffer = $data;\n" " }\n" - " return 100;\n" + " return strlen($buffer);\n" "}\n" "\n" "function swoole_socket_bind(Socket $socket, string $address, int $port = 0): bool\n" @@ -8510,9 +9806,9 @@ static const char* swoole_library_source_ext_sockets = " return $socket->getOption($level, $optname);\n" "}\n" "\n" - "function swoole_socket_shutdown(Socket $socket, int $how = 2)\n" + "function swoole_socket_shutdown(Socket $socket, int $how = 2): bool\n" "{\n" - " $socket->shutdown($how);\n" + " return $socket->shutdown($how);\n" "}\n" "\n" "function swoole_socket_close(Socket $socket)\n" @@ -8520,7 +9816,7 @@ static const char* swoole_library_source_ext_sockets = " $socket->close();\n" "}\n" "\n" - "function swoole_socket_clear_error(Socket $socket = null)\n" + "function swoole_socket_clear_error(?Socket $socket = null)\n" "{\n" " if ($socket) {\n" " $socket->errCode = 0;\n" @@ -8528,7 +9824,7 @@ static const char* swoole_library_source_ext_sockets = " swoole_clear_error();\n" "}\n" "\n" - "function swoole_socket_last_error(Socket $socket = null): int\n" + "function swoole_socket_last_error(?Socket $socket = null): int\n" "{\n" " if ($socket) {\n" " return $socket->errCode;\n" @@ -8538,22 +9834,50 @@ static const char* swoole_library_source_ext_sockets = "\n" "function swoole_socket_set_block(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" - " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout);\n" + " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout); // @phpstan-ignore property.notFound\n" " }\n" - " $socket->__ext_sockets_nonblock = false;\n" + " $socket->__ext_sockets_nonblock = false; // @phpstan-ignore property.notFound\n" " return true;\n" "}\n" "\n" "function swoole_socket_set_nonblock(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" " return true;\n" " }\n" - " $socket->__ext_sockets_nonblock = true;\n" - " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" + " $socket->__ext_sockets_nonblock = true; // @phpstan-ignore property.notFound\n" + " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO); // @phpstan-ignore property.notFound\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\n" " return true;\n" + "}\n" + "\n" + "function swoole_socket_create_pair(\n" + " int $domain,\n" + " int $type,\n" + " int $protocol,\n" + " array &$pair,\n" + ") {\n" + " $_pair = swoole_coroutine_socketpair($domain, $type, $protocol);\n" + " if ($_pair) {\n" + " $pair = $_pair;\n" + " return true;\n" + " }\n" + " return false;\n" + "}\n" + "\n" + "/**\n" + " * @since 5.0.0\n" + " */\n" + "function swoole_socket_import_stream(mixed $stream): Socket|false\n" + "{\n" + " return Socket::import($stream); // @phpstan-ignore staticMethod.notFound\n" "}\n"; static const char* swoole_library_source_functions = @@ -8568,11 +9892,11 @@ static const char* swoole_library_source_functions = "\n" "declare(strict_types=1);\n" "\n" - "if (PHP_VERSION_ID < 70200) {\n" - " throw new RuntimeException('require PHP version 7.2 or later');\n" + "if (PHP_VERSION_ID < 80100) { // @phpstan-ignore smaller.alwaysFalse\n" + " throw new RuntimeException('require PHP version 8.1 or later');\n" "}\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function _string(string $string = ''): Swoole\\StringObject\n" " {\n" " return new Swoole\\StringObject($string);\n" @@ -8589,6 +9913,37 @@ static const char* swoole_library_source_functions = " }\n" "}\n" "\n" + "class SwooleLibrary\n" + "{\n" + " /**\n" + " * @var array\n" + " */\n" + " public static array $options = [];\n" + "}\n" + "\n" + "/**\n" + " * @param array $options\n" + " */\n" + "function swoole_library_set_options(array $options): void\n" + "{\n" + " SwooleLibrary::$options = $options;\n" + "}\n" + "\n" + "function swoole_library_get_options(): array\n" + "{\n" + " return SwooleLibrary::$options;\n" + "}\n" + "\n" + "function swoole_library_set_option(string $key, mixed $value): void\n" + "{\n" + " SwooleLibrary::$options[$key] = $value;\n" + "}\n" + "\n" + "function swoole_library_get_option(string $key): mixed\n" + "{\n" + " return SwooleLibrary::$options[$key] ?? null;\n" + "}\n" + "\n" "function swoole_string(string $string = ''): Swoole\\StringObject\n" "{\n" " return new Swoole\\StringObject($string);\n" @@ -8611,33 +9966,32 @@ static const char* swoole_library_source_functions = " $table = new Swoole\\Table($size, 0.25);\n" "\n" " foreach ($_fields as $f) {\n" - " $_f = swoole_string($f)->trim()->split(':');\n" + " $_f = swoole_string($f)->trim()->split(':');\n" " $name = $_f->get(0)->trim()->toString();\n" " $type = $_f->get(1)->trim();\n" "\n" " switch ($type) {\n" - " case 'i':\n" - " case 'int':\n" - " $table->column($name, Swoole\\Table::TYPE_INT);\n" - " break;\n" - " case 'f':\n" - " case 'float':\n" - " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" - " break;\n" - " case 's':\n" - " case 'string':\n" - " if ($_f->count() < 3) {\n" - " throw new RuntimeException('need to give string length');\n" - " }\n" - " $length = intval($_f->get(2)->trim()->toString());\n" - " if ($length <= 0) {\n" - " throw new RuntimeException(\"invalid string length[{$length}]\");\n" - " }\n" - " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" - " break;\n" - " default:\n" - " throw new RuntimeException(\"unknown field type[{$type}]\");\n" - " break;\n" + " case 'i':\n" + " case 'int':\n" + " $table->column($name, Swoole\\Table::TYPE_INT);\n" + " break;\n" + " case 'f':\n" + " case 'float':\n" + " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" + " break;\n" + " case 's':\n" + " case 'string':\n" + " if ($_f->count() < 3) {\n" + " throw new RuntimeException('need to give string length');\n" + " }\n" + " $length = (int) $_f->get(2)->trim()->toString();\n" + " if ($length <= 0) {\n" + " throw new RuntimeException(\"invalid string length[{$length}]\");\n" + " }\n" + " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" + " break;\n" + " default:\n" + " throw new RuntimeException(\"unknown field type[{$type}]\");\n" " }\n" " }\n" "\n" @@ -8658,24 +10012,40 @@ static const char* swoole_library_source_functions = " return array_key_exists($key, $array) ? $array[$key] : $default_value;\n" "}\n" "\n" - "if (!function_exists('array_key_last')) {\n" - " function array_key_last(array $array)\n" - " {\n" - " if (!empty($array)) {\n" - " return key(array_slice($array, -1, 1, true));\n" - " }\n" - " return null;\n" - " }\n" + "function swoole_is_in_container()\n" + "{\n" + " $mountinfo = file_get_contents('/proc/self/mountinfo');\n" + " return strpos($mountinfo, 'kubepods') > 0 || strpos($mountinfo, 'docker') > 0;\n" "}\n" "\n" - "if (!function_exists('array_key_first')) {\n" - " function array_key_first(array $array)\n" - " {\n" - " foreach ($array as $key => $unused) {\n" - " return $key;\n" - " }\n" - " return null;\n" + "function swoole_container_cpu_num()\n" + "{\n" + " $swoole_cpu_num = intval(getenv('SWOOLE_CPU_NUM'));\n" + " if ($swoole_cpu_num > 0) {\n" + " return $swoole_cpu_num;\n" + " }\n" + " if (!swoole_is_in_container()) {\n" + " return swoole_cpu_num();\n" + " }\n" + " // cgroup v2\n" + " $cpu_max = '/sys/fs/cgroup/cpu.max';\n" + " if (file_exists($cpu_max)) {\n" + " $cpu_max = file_get_contents($cpu_max);\n" + " $fields = explode($cpu_max, ' ');\n" + " $quota_us = $fields[0];\n" + " if ($quota_us === 'max') { // @phpstan-ignore identical.alwaysFalse\n" + " return swoole_cpu_num();\n" + " }\n" + " $period_us = $fields[1] ?? 100000;\n" + " } else {\n" + " $quota_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\n" + " $period_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us');\n" + " }\n" + " $cpu_num = floatval($quota_us) / floatval($period_us);\n" + " if ($cpu_num < 1) {\n" + " return swoole_cpu_num();\n" " }\n" + " return intval(floor($cpu_num));\n" "}\n"; static const char* swoole_library_source_alias = @@ -8690,7 +10060,7 @@ static const char* swoole_library_source_alias = "\n" "declare(strict_types=1);\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " class_alias(Swoole\\Coroutine\\WaitGroup::class, Co\\WaitGroup::class, true);\n" " class_alias(Swoole\\Coroutine\\Server::class, Co\\Server::class, true);\n" " class_alias(Swoole\\Coroutine\\Server\\Connection::class, Co\\Server\\Connection::class, true);\n" @@ -8717,7 +10087,7 @@ static const char* swoole_library_source_alias_ns = "\n" "use Swoole\\Coroutine;\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function run(callable $fn, ...$args)\n" " {\n" " return \\Swoole\\Coroutine\\run($fn, ...$args);\n" @@ -8736,64 +10106,74 @@ static const char* swoole_library_source_alias_ns = void php_swoole_load_library() { - zend::eval(swoole_library_source_constants, "@swoole-src/library/constants.php"); - zend::eval(swoole_library_source_std_exec, "@swoole-src/library/std/exec.php"); - zend::eval(swoole_library_source_core_constant, "@swoole-src/library/core/Constant.php"); - zend::eval(swoole_library_source_core_string_object, "@swoole-src/library/core/StringObject.php"); - zend::eval(swoole_library_source_core_multibyte_string_object, "@swoole-src/library/core/MultibyteStringObject.php"); - zend::eval(swoole_library_source_core_exception_array_key_not_exists, "@swoole-src/library/core/Exception/ArrayKeyNotExists.php"); - zend::eval(swoole_library_source_core_array_object, "@swoole-src/library/core/ArrayObject.php"); - zend::eval(swoole_library_source_core_object_proxy, "@swoole-src/library/core/ObjectProxy.php"); - zend::eval(swoole_library_source_core_coroutine_wait_group, "@swoole-src/library/core/Coroutine/WaitGroup.php"); - zend::eval(swoole_library_source_core_coroutine_server, "@swoole-src/library/core/Coroutine/Server.php"); - zend::eval(swoole_library_source_core_coroutine_server_connection, "@swoole-src/library/core/Coroutine/Server/Connection.php"); - zend::eval(swoole_library_source_core_coroutine_barrier, "@swoole-src/library/core/Coroutine/Barrier.php"); - zend::eval(swoole_library_source_core_coroutine_http_functions, "@swoole-src/library/core/Coroutine/Http/functions.php"); - zend::eval(swoole_library_source_core_connection_pool, "@swoole-src/library/core/ConnectionPool.php"); - zend::eval(swoole_library_source_core_database_object_proxy, "@swoole-src/library/core/Database/ObjectProxy.php"); - zend::eval(swoole_library_source_core_database_mysqli_config, "@swoole-src/library/core/Database/MysqliConfig.php"); - zend::eval(swoole_library_source_core_database_mysqli_exception, "@swoole-src/library/core/Database/MysqliException.php"); - zend::eval(swoole_library_source_core_database_mysqli_pool, "@swoole-src/library/core/Database/MysqliPool.php"); - zend::eval(swoole_library_source_core_database_mysqli_proxy, "@swoole-src/library/core/Database/MysqliProxy.php"); - zend::eval(swoole_library_source_core_database_mysqli_statement_proxy, "@swoole-src/library/core/Database/MysqliStatementProxy.php"); - zend::eval(swoole_library_source_core_database_pdo_config, "@swoole-src/library/core/Database/PDOConfig.php"); - zend::eval(swoole_library_source_core_database_pdo_pool, "@swoole-src/library/core/Database/PDOPool.php"); - zend::eval(swoole_library_source_core_database_pdo_proxy, "@swoole-src/library/core/Database/PDOProxy.php"); - zend::eval(swoole_library_source_core_database_pdo_statement_proxy, "@swoole-src/library/core/Database/PDOStatementProxy.php"); - zend::eval(swoole_library_source_core_database_redis_config, "@swoole-src/library/core/Database/RedisConfig.php"); - zend::eval(swoole_library_source_core_database_redis_pool, "@swoole-src/library/core/Database/RedisPool.php"); - zend::eval(swoole_library_source_core_http_status, "@swoole-src/library/core/Http/Status.php"); - zend::eval(swoole_library_source_core_curl_exception, "@swoole-src/library/core/Curl/Exception.php"); - zend::eval(swoole_library_source_core_curl_handler, "@swoole-src/library/core/Curl/Handler.php"); - zend::eval(swoole_library_source_core_fast_cgi, "@swoole-src/library/core/FastCGI.php"); - zend::eval(swoole_library_source_core_fast_cgi_record, "@swoole-src/library/core/FastCGI/Record.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_params, "@swoole-src/library/core/FastCGI/Record/Params.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_abort_request, "@swoole-src/library/core/FastCGI/Record/AbortRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_begin_request, "@swoole-src/library/core/FastCGI/Record/BeginRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_data, "@swoole-src/library/core/FastCGI/Record/Data.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_end_request, "@swoole-src/library/core/FastCGI/Record/EndRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_get_values, "@swoole-src/library/core/FastCGI/Record/GetValues.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_get_values_result, "@swoole-src/library/core/FastCGI/Record/GetValuesResult.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stdin, "@swoole-src/library/core/FastCGI/Record/Stdin.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stdout, "@swoole-src/library/core/FastCGI/Record/Stdout.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stderr, "@swoole-src/library/core/FastCGI/Record/Stderr.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_unknown_type, "@swoole-src/library/core/FastCGI/Record/UnknownType.php"); - zend::eval(swoole_library_source_core_fast_cgi_frame_parser, "@swoole-src/library/core/FastCGI/FrameParser.php"); - zend::eval(swoole_library_source_core_fast_cgi_message, "@swoole-src/library/core/FastCGI/Message.php"); - zend::eval(swoole_library_source_core_fast_cgi_request, "@swoole-src/library/core/FastCGI/Request.php"); - zend::eval(swoole_library_source_core_fast_cgi_response, "@swoole-src/library/core/FastCGI/Response.php"); - zend::eval(swoole_library_source_core_fast_cgi_http_request, "@swoole-src/library/core/FastCGI/HttpRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_http_response, "@swoole-src/library/core/FastCGI/HttpResponse.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_client, "@swoole-src/library/core/Coroutine/FastCGI/Client.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_client_exception, "@swoole-src/library/core/Coroutine/FastCGI/Client/Exception.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_proxy, "@swoole-src/library/core/Coroutine/FastCGI/Proxy.php"); - zend::eval(swoole_library_source_core_process_manager, "@swoole-src/library/core/Process/Manager.php"); - zend::eval(swoole_library_source_core_server_admin, "@swoole-src/library/core/Server/Admin.php"); - zend::eval(swoole_library_source_core_server_helper, "@swoole-src/library/core/Server/Helper.php"); - zend::eval(swoole_library_source_core_coroutine_functions, "@swoole-src/library/core/Coroutine/functions.php"); - zend::eval(swoole_library_source_ext_curl, "@swoole-src/library/ext/curl.php"); - zend::eval(swoole_library_source_ext_sockets, "@swoole-src/library/ext/sockets.php"); - zend::eval(swoole_library_source_functions, "@swoole-src/library/functions.php"); - zend::eval(swoole_library_source_alias, "@swoole-src/library/alias.php"); - zend::eval(swoole_library_source_alias_ns, "@swoole-src/library/alias_ns.php"); + _eval(swoole_library_source_constants, "@swoole/library/constants.php"); + _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); + _eval(swoole_library_source_core_constant, "@swoole/library/core/Constant.php"); + _eval(swoole_library_source_core_string_object, "@swoole/library/core/StringObject.php"); + _eval(swoole_library_source_core_multibyte_string_object, "@swoole/library/core/MultibyteStringObject.php"); + _eval(swoole_library_source_core_exception_array_key_not_exists, "@swoole/library/core/Exception/ArrayKeyNotExists.php"); + _eval(swoole_library_source_core_array_object, "@swoole/library/core/ArrayObject.php"); + _eval(swoole_library_source_core_object_proxy, "@swoole/library/core/ObjectProxy.php"); + _eval(swoole_library_source_core_coroutine_wait_group, "@swoole/library/core/Coroutine/WaitGroup.php"); + _eval(swoole_library_source_core_coroutine_server, "@swoole/library/core/Coroutine/Server.php"); + _eval(swoole_library_source_core_coroutine_server_connection, "@swoole/library/core/Coroutine/Server/Connection.php"); + _eval(swoole_library_source_core_coroutine_barrier, "@swoole/library/core/Coroutine/Barrier.php"); + _eval(swoole_library_source_core_coroutine_http_client_proxy, "@swoole/library/core/Coroutine/Http/ClientProxy.php"); + _eval(swoole_library_source_core_coroutine_http_functions, "@swoole/library/core/Coroutine/Http/functions.php"); + _eval(swoole_library_source_core_connection_pool, "@swoole/library/core/ConnectionPool.php"); + _eval(swoole_library_source_core_database_object_proxy, "@swoole/library/core/Database/ObjectProxy.php"); + _eval(swoole_library_source_core_database_mysqli_config, "@swoole/library/core/Database/MysqliConfig.php"); + _eval(swoole_library_source_core_database_mysqli_exception, "@swoole/library/core/Database/MysqliException.php"); + _eval(swoole_library_source_core_database_mysqli_pool, "@swoole/library/core/Database/MysqliPool.php"); + _eval(swoole_library_source_core_database_mysqli_proxy, "@swoole/library/core/Database/MysqliProxy.php"); + _eval(swoole_library_source_core_database_mysqli_statement_proxy, "@swoole/library/core/Database/MysqliStatementProxy.php"); + _eval(swoole_library_source_core_database_detects_lost_connections, "@swoole/library/core/Database/DetectsLostConnections.php"); + _eval(swoole_library_source_core_database_pdo_config, "@swoole/library/core/Database/PDOConfig.php"); + _eval(swoole_library_source_core_database_pdo_pool, "@swoole/library/core/Database/PDOPool.php"); + _eval(swoole_library_source_core_database_pdo_proxy, "@swoole/library/core/Database/PDOProxy.php"); + _eval(swoole_library_source_core_database_pdo_statement_proxy, "@swoole/library/core/Database/PDOStatementProxy.php"); + _eval(swoole_library_source_core_database_redis_config, "@swoole/library/core/Database/RedisConfig.php"); + _eval(swoole_library_source_core_database_redis_pool, "@swoole/library/core/Database/RedisPool.php"); + _eval(swoole_library_source_core_http_status, "@swoole/library/core/Http/Status.php"); + _eval(swoole_library_source_core_curl_exception, "@swoole/library/core/Curl/Exception.php"); + _eval(swoole_library_source_core_curl_handler, "@swoole/library/core/Curl/Handler.php"); + _eval(swoole_library_source_core_fast_cgi, "@swoole/library/core/FastCGI.php"); + _eval(swoole_library_source_core_fast_cgi_record, "@swoole/library/core/FastCGI/Record.php"); + _eval(swoole_library_source_core_fast_cgi_record_params, "@swoole/library/core/FastCGI/Record/Params.php"); + _eval(swoole_library_source_core_fast_cgi_record_abort_request, "@swoole/library/core/FastCGI/Record/AbortRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_begin_request, "@swoole/library/core/FastCGI/Record/BeginRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_data, "@swoole/library/core/FastCGI/Record/Data.php"); + _eval(swoole_library_source_core_fast_cgi_record_end_request, "@swoole/library/core/FastCGI/Record/EndRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_get_values, "@swoole/library/core/FastCGI/Record/GetValues.php"); + _eval(swoole_library_source_core_fast_cgi_record_get_values_result, "@swoole/library/core/FastCGI/Record/GetValuesResult.php"); + _eval(swoole_library_source_core_fast_cgi_record_stdin, "@swoole/library/core/FastCGI/Record/Stdin.php"); + _eval(swoole_library_source_core_fast_cgi_record_stdout, "@swoole/library/core/FastCGI/Record/Stdout.php"); + _eval(swoole_library_source_core_fast_cgi_record_stderr, "@swoole/library/core/FastCGI/Record/Stderr.php"); + _eval(swoole_library_source_core_fast_cgi_record_unknown_type, "@swoole/library/core/FastCGI/Record/UnknownType.php"); + _eval(swoole_library_source_core_fast_cgi_frame_parser, "@swoole/library/core/FastCGI/FrameParser.php"); + _eval(swoole_library_source_core_fast_cgi_message, "@swoole/library/core/FastCGI/Message.php"); + _eval(swoole_library_source_core_fast_cgi_request, "@swoole/library/core/FastCGI/Request.php"); + _eval(swoole_library_source_core_fast_cgi_response, "@swoole/library/core/FastCGI/Response.php"); + _eval(swoole_library_source_core_fast_cgi_http_request, "@swoole/library/core/FastCGI/HttpRequest.php"); + _eval(swoole_library_source_core_fast_cgi_http_response, "@swoole/library/core/FastCGI/HttpResponse.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_client, "@swoole/library/core/Coroutine/FastCGI/Client.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_client_exception, "@swoole/library/core/Coroutine/FastCGI/Client/Exception.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_proxy, "@swoole/library/core/Coroutine/FastCGI/Proxy.php"); + _eval(swoole_library_source_core_process_manager, "@swoole/library/core/Process/Manager.php"); + _eval(swoole_library_source_core_server_admin, "@swoole/library/core/Server/Admin.php"); + _eval(swoole_library_source_core_server_helper, "@swoole/library/core/Server/Helper.php"); + _eval(swoole_library_source_core_name_resolver, "@swoole/library/core/NameResolver.php"); + _eval(swoole_library_source_core_name_resolver_exception, "@swoole/library/core/NameResolver/Exception.php"); + _eval(swoole_library_source_core_name_resolver_cluster, "@swoole/library/core/NameResolver/Cluster.php"); + _eval(swoole_library_source_core_name_resolver_redis, "@swoole/library/core/NameResolver/Redis.php"); + _eval(swoole_library_source_core_name_resolver_nacos, "@swoole/library/core/NameResolver/Nacos.php"); + _eval(swoole_library_source_core_name_resolver_consul, "@swoole/library/core/NameResolver/Consul.php"); + _eval(swoole_library_source_core_thread_pool, "@swoole/library/core/Thread/Pool.php"); + _eval(swoole_library_source_core_thread_runnable, "@swoole/library/core/Thread/Runnable.php"); + _eval(swoole_library_source_core_coroutine_functions, "@swoole/library/core/Coroutine/functions.php"); + _eval(swoole_library_source_ext_curl, "@swoole/library/ext/curl.php"); + _eval(swoole_library_source_ext_sockets, "@swoole/library/ext/sockets.php"); + _eval(swoole_library_source_functions, "@swoole/library/functions.php"); + _eval(swoole_library_source_alias, "@swoole/library/alias.php"); + _eval(swoole_library_source_alias_ns, "@swoole/library/alias_ns.php"); } diff --git a/ext-src/php_swoole_mysql_proto.h b/ext-src/php_swoole_mysql_proto.h deleted file mode 100644 index 95b71c8c85d..00000000000 --- a/ext-src/php_swoole_mysql_proto.h +++ /dev/null @@ -1,1007 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "php_swoole_cxx.h" -#include "swoole_util.h" - -#ifdef SW_USE_OPENSSL -#ifndef OPENSSL_NO_RSA -#define SW_MYSQL_RSA_SUPPORT -#include -#include -#include -#endif -#endif - -enum sw_mysql_command -{ - SW_MYSQL_COM_NULL = -1, - SW_MYSQL_COM_SLEEP = 0, - SW_MYSQL_COM_QUIT, - SW_MYSQL_COM_INIT_DB, - SW_MYSQL_COM_QUERY = 3, - SW_MYSQL_COM_FIELD_LIST, - SW_MYSQL_COM_CREATE_DB, - SW_MYSQL_COM_DROP_DB, - SW_MYSQL_COM_REFRESH, - SW_MYSQL_COM_SHUTDOWN, - SW_MYSQL_COM_STATISTICS, - SW_MYSQL_COM_PROCESS_INFO, - SW_MYSQL_COM_CONNECT, - SW_MYSQL_COM_PROCESS_KILL, - SW_MYSQL_COM_DEBUG, - SW_MYSQL_COM_PING, - SW_MYSQL_COM_TIME, - SW_MYSQL_COM_DELAYED_INSERT, - SW_MYSQL_COM_CHANGE_USER, - SW_MYSQL_COM_BINLOG_DUMP, - SW_MYSQL_COM_TABLE_DUMP, - SW_MYSQL_COM_CONNECT_OUT, - SW_MYSQL_COM_REGISTER_SLAVE, - SW_MYSQL_COM_STMT_PREPARE, - SW_MYSQL_COM_STMT_EXECUTE, - SW_MYSQL_COM_STMT_SEND_LONG_DATA, - SW_MYSQL_COM_STMT_CLOSE, - SW_MYSQL_COM_STMT_RESET, - SW_MYSQL_COM_SET_OPTION, - SW_MYSQL_COM_STMT_FETCH, - SW_MYSQL_COM_DAEMON, - SW_MYSQL_COM_END -}; - -enum sw_mysql_handshake_state -{ - SW_MYSQL_HANDSHAKE_WAIT_REQUEST, - SW_MYSQL_HANDSHAKE_WAIT_SWITCH, - SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE, - SW_MYSQL_HANDSHAKE_WAIT_RSA, - SW_MYSQL_HANDSHAKE_WAIT_RESULT, - SW_MYSQL_HANDSHAKE_COMPLETED, -}; - -#define SW_MYSQL_AUTH_SIGNATRUE_PACKET_LENGTH 2 - -enum sw_mysql_auth_signature -{ - SW_MYSQL_AUTH_SIGNATURE_ERROR = 0x00, // get signature failed - SW_MYSQL_AUTH_SIGNATURE = 0x01, - SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED = 0x02, - SW_MYSQL_AUTH_SIGNATURE_SUCCESS = 0x03, - SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED = 0x04, // rsa required -}; - -enum sw_mysql_command_flag -{ - SW_MYSQL_COMMAND_FLAG_QUERY = 1 << 4, - SW_MYSQL_COMMAND_FLAG_EXECUTE = 1 << 5, -}; - -enum sw_mysql_state -{ - SW_MYSQL_STATE_CLOSED = 0, - SW_MYSQL_STATE_IDLE = 1, - SW_MYSQL_STATE_QUERY = 2 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_FETCH = 3 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_MORE_RESULTS = 4 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_PREPARE = 5 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_EXECUTE = 6 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_FETCH = 7 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_MORE_RESULTS = 8 | SW_MYSQL_COMMAND_FLAG_EXECUTE, -}; - -enum sw_mysql_packet_types -{ - SW_MYSQL_PACKET_OK = 0x0, - SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST = 0x01, - - /* not defined in protocol */ - SW_MYSQL_PACKET_RAW_DATA, - SW_MYSQL_PACKET_GREETING, - SW_MYSQL_PACKET_LOGIN, - SW_MYSQL_PACKET_AUTH_SWITCH_RESPONSE, - SW_MYSQL_PACKET_AUTH_SIGNATURE_RESPONSE, - SW_MYSQL_PACKET_LCB, // length coded binary - SW_MYSQL_PACKET_FIELD, - SW_MYSQL_PACKET_ROW_DATA, - SW_MYSQL_PACKET_PREPARE_STATEMENT, - /* ======================= */ - - SW_MYSQL_PACKET_NULL = 0xfb, - SW_MYSQL_PACKET_EOF = 0xfe, - SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST = 0xfe, - SW_MYSQL_PACKET_ERR = 0xff -}; - -enum sw_mysql_field_types -{ - SW_MYSQL_TYPE_DECIMAL, - SW_MYSQL_TYPE_TINY, - SW_MYSQL_TYPE_SHORT, - SW_MYSQL_TYPE_LONG, - SW_MYSQL_TYPE_FLOAT, - SW_MYSQL_TYPE_DOUBLE, - SW_MYSQL_TYPE_NULL, - SW_MYSQL_TYPE_TIMESTAMP, - SW_MYSQL_TYPE_LONGLONG, - SW_MYSQL_TYPE_INT24, - SW_MYSQL_TYPE_DATE, - SW_MYSQL_TYPE_TIME, - SW_MYSQL_TYPE_DATETIME, - SW_MYSQL_TYPE_YEAR, - SW_MYSQL_TYPE_NEWDATE, - SW_MYSQL_TYPE_VARCHAR, - SW_MYSQL_TYPE_BIT, - SW_MYSQL_TYPE_JSON = 245, - SW_MYSQL_TYPE_NEWDECIMAL, - SW_MYSQL_TYPE_ENUM, - SW_MYSQL_TYPE_SET, - SW_MYSQL_TYPE_TINY_BLOB, - SW_MYSQL_TYPE_MEDIUM_BLOB, - SW_MYSQL_TYPE_LONG_BLOB, - SW_MYSQL_TYPE_BLOB, - SW_MYSQL_TYPE_VAR_STRING, - SW_MYSQL_TYPE_STRING, - SW_MYSQL_TYPE_GEOMETRY -}; - -// ref: https://dev.mysql.com/doc/dev/mysql-server/8.0.0/group__group__cs__capabilities__flags.html -// use regex: "\#define[ ]+(CLIENT_[A-Z_\d]+)[ ]+(\(?[\dA-Z <]+\)?)\n[ ]+?[ ]+([\s\S ]+?\.) More\.\.\.\n?" -// to "SW_MYSQL_$1 = $2, /* $3 */" -enum sw_mysql_client_capability_flags -{ - SW_MYSQL_CLIENT_LONG_PASSWORD = 1, /* Use the improved version of Old Password Authentication. */ - SW_MYSQL_CLIENT_FOUND_ROWS = 2, /* Send found rows instead of affected rows in EOF_Packet. */ - SW_MYSQL_CLIENT_LONG_FLAG = 4, /* Get all column flags. */ - SW_MYSQL_CLIENT_CONNECT_WITH_DB = 8, /* Database (schema) name can be specified on connect in Handshake Response Packet. */ - SW_MYSQL_CLIENT_NO_SCHEMA = 16, /* Don't allow database.table.column. */ - SW_MYSQL_CLIENT_COMPRESS = 32, /* Compression protocol supported. */ - SW_MYSQL_CLIENT_ODBC = 64, /* Special handling of ODBC behavior. */ - SW_MYSQL_CLIENT_LOCAL_FILES = 128, /* Can use LOAD DATA LOCAL. */ - SW_MYSQL_CLIENT_IGNORE_SPACE = 256, /* Ignore spaces before '('. */ - SW_MYSQL_CLIENT_PROTOCOL_41 = 512, /* New 4.1 protocol. */ - SW_MYSQL_CLIENT_INTERACTIVE = 1024, /* This is an interactive client. */ - SW_MYSQL_CLIENT_SSL = 2048, /* Use SSL encryption for the session. */ - SW_MYSQL_CLIENT_IGNORE_SIGPIPE = 4096, /* Client only flag. */ - SW_MYSQL_CLIENT_TRANSACTIONS = 8192, /* Client knows about transactions. */ - SW_MYSQL_CLIENT_RESERVED = 16384, /* flag for 4.1 protocol. */ - SW_MYSQL_CLIENT_SECURE_CONNECTION = 32768, /* swoole custom name for RESERVED2. */ - SW_MYSQL_CLIENT_RESERVED2 = 32768, /* flag for 4.1 authentication. */ - SW_MYSQL_CLIENT_MULTI_STATEMENTS = (1UL << 16), /* Enable/disable multi-stmt support. */ - SW_MYSQL_CLIENT_MULTI_RESULTS = (1UL << 17), /* Enable/disable multi-results. */ - SW_MYSQL_CLIENT_PS_MULTI_RESULTS = (1UL << 18), /* Multi-results and OUT parameters in PS-protocol. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH = (1UL << 19), /* Client supports plugin authentication. */ - SW_MYSQL_CLIENT_CONNECT_ATTRS = (1UL << 20), /* Client supports connection attributes. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21), /* Enable authentication response packet to be larger than 255 bytes. */ - SW_MYSQL_CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22), /* Don't close the connection for a user account with expired password. */ - SW_MYSQL_CLIENT_SESSION_TRACK = (1UL << 23), /* Capable of handling server state change information. */ - SW_MYSQL_CLIENT_DEPRECATE_EOF = (1UL << 24), /* Client no longer needs EOF_Packet and will use OK_Packet instead. */ - SW_MYSQL_CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30), /* Verify server certificate. */ - SW_MYSQL_CLIENT_REMEMBER_OPTIONS = (1UL << 31) /* Don't reset the options after an unsuccessful connect. */ -}; - -// ref: https://dev.mysql.com/doc/internals/en/status-flags.html -enum sw_mysql_server_status_flags -{ - SW_MYSQL_SERVER_STATUS_IN_TRANS = 0x0001, // a transaction is active - SW_MYSQL_SERVER_STATUS_AUTOCOMMIT = 0x0002, //auto-commit is enabled - SW_MYSQL_SERVER_MORE_RESULTS_EXISTS = 0x0008, - SW_MYSQL_SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010, - SW_MYSQL_SERVER_STATUS_NO_INDEX_USED = 0x0020, - SW_MYSQL_SERVER_STATUS_CURSOR_EXISTS = 0x0040, // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH must be used to fetch the row-data. - SW_MYSQL_SERVER_STATUS_LAST_ROW_SENT = 0x0080, - SW_MYSQL_SERVER_STATUS_DB_DROPPED = 0x0100, - SW_MYSQL_SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200, - SW_MYSQL_SERVER_STATUS_METADATA_CHANGED = 0x0400, - SW_MYSQL_SERVER_QUERY_WAS_SLOW = 0x0800, - SW_MYSQL_SERVER_PS_OUT_PARAMS = 0x1000, - SW_MYSQL_SERVER_STATUS_IN_TRANS_READONLY = 0x2000, // in a read-only transaction - SW_MYSQL_SERVER_SESSION_STATE_CHANGED = 0x4000 // connection state information has changed -}; - -#define SW_MYSQL_NO_RSA_ERROR "MySQL8 caching_sha2_password authentication plugin need enable OpenSSL support" - -#define SW_MYSQL_NOT_NULL_FLAG 1 -#define SW_MYSQL_PRI_KEY_FLAG 2 -#define SW_MYSQL_UNIQUE_KEY_FLAG 4 -#define SW_MYSQL_MULTIPLE_KEY_FLAG 8 -#define SW_MYSQL_BLOB_FLAG 16 -#define SW_MYSQL_UNSIGNED_FLAG 32 -#define SW_MYSQL_ZEROFILL_FLAG 64 -#define SW_MYSQL_BINARY_FLAG 128 -#define SW_MYSQL_ENUM_FLAG 256 -#define SW_MYSQL_AUTO_INCREMENT_FLAG 512 -#define SW_MYSQL_TIMESTAMP_FLAG 1024 -#define SW_MYSQL_SET_FLAG 2048 -#define SW_MYSQL_NO_DEFAULT_VALUE_FLAG 4096 -#define SW_MYSQL_ON_UPDATE_NOW_FLAG 8192 -#define SW_MYSQL_PART_KEY_FLAG 16384 -#define SW_MYSQL_GROUP_FLAG 32768 -#define SW_MYSQL_NUM_FLAG 32768 - -/* int<3> payload_length + int<1> sequence_id */ -#define SW_MYSQL_PACKET_HEADER_SIZE 4 -#define SW_MYSQL_PACKET_TYPE_OFFSET 5 -#define SW_MYSQL_PACKET_EOF_MAX_SIZE 9 -#define SW_MYSQL_PACKET_PREPARED_OK_SIZE 12 -#define SW_MYSQL_MAX_PACKET_BODY_SIZE 0x00ffffff -#define SW_MYSQL_MAX_PACKET_SIZE (SW_MYSQL_PACKET_HEADER_SIZE + SW_MYSQL_MAX_PACKET_BODY_SIZE) - -// nonce: a number or bit string used only once, in security engineering -// other names on doc: challenge/scramble/salt -#define SW_MYSQL_NONCE_LENGTH 20 - -// clang-format off -#define sw_mysql_uint2korr2korr(A) (uint16_t) (((uint16_t) ((uchar) (A)[0])) +\ - ((uint16_t) ((uchar) (A)[1]) << 8)) -#define sw_mysql_uint2korr3korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16)) -#define sw_mysql_uint2korr4korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) -#define sw_mysql_uint2korr8korr(A) ((uint64_t)(((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) +\ - (((uint64_t) (((uint32_t) ((uchar) (A)[4])) +\ - (((uint32_t) ((uchar) (A)[5])) << 8) +\ - (((uint32_t) ((uchar) (A)[6])) << 16) +\ - (((uint32_t) ((uchar) (A)[7])) << 24))) << 32)) - -#define sw_mysql_int1store(T,A) do { *((int8_t*) (T)) = (int8_t)(A); } while(0) -#define sw_mysql_int2store(T,A) do { uint32_t def_temp= (uint32_t) (A) ;\ - *((uchar*) (T)) = (uchar)(def_temp); \ - *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0) -#define sw_mysql_int3store(T,A) do { /*lint -save -e734 */\ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16)); \ - /*lint -restore */} while (0) -#define sw_mysql_int4store(T,A) do { \ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16));\ - *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0) -#define sw_mysql_int5store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0) -/* Based on int5store() from Andrey Hristov */ -#define sw_mysql_int6store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); \ - *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0) - -// clang-format on - -#define sw_mysql_int8store(T,A) do { \ - uint32_t def_temp= (uint32_t) (A), def_temp2= (uint32_t) ((A) >> 32); \ - sw_mysql_int4store((T),def_temp); \ - sw_mysql_int4store((T+4),def_temp2); } while (0) - -#define sw_mysql_doublestore(T,A) do { \ - double def_temp = (double) A; \ - memcpy(T, &def_temp, sizeof(double)); \ - } while (0) - -#if defined(SW_DEBUG) && defined(SW_LOG_TRACE_OPEN) -#define swMysqlPacketDump(length, number, data, title) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (SW_TRACE_MYSQL_CLIENT & SwooleG.trace_flags)) \ - { \ - swoole_debug("+----------+------------+-------------------------------------------------------+"); \ - swoole_debug("| P#%-6u | L%-9u | %-10u %42s |", number, SW_MYSQL_PACKET_HEADER_SIZE + length, length, title); \ - swoole_hex_dump(data, length); \ - } -#else -#define swMysqlPacketDump(length, number, data, title) -#endif - -namespace swoole { namespace mysql { -//-----------------------------------namespace begin-------------------------------------------- -char get_charset(const char *name); -uint8_t get_static_type_size(uint8_t type); - -inline uint8_t read_lcb_size(const char *p) -{ - switch ((uchar) p[0]) - { - case 251: - return 1; - case 252: - return 3; - case 253: - return 4; - case 254: - return 9; - default: - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint64_t *length, bool *nul) -{ - switch ((uchar) p[0]) - { - case 251: /* fb : 1 octet */ - *length = 0; - *nul = true; - return 1; - case 252: /* fc : 2 octets */ - *length = sw_mysql_uint2korr2korr(p + 1); - *nul = false; - return 3; - case 253: /* fd : 3 octets */ - *length = sw_mysql_uint2korr3korr(p + 1); - *nul = false; - return 4; - case 254: /* fe : 8 octets */ - *length = sw_mysql_uint2korr8korr(p + 1); - *nul = false; - return 9; - default: - *length = (uchar) p[0]; - *nul = false; - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint32_t *length, bool *nul) -{ - uint64_t _r; - uint8_t ret = read_lcb(p, &_r, nul); - *length = _r; - return ret; -} - -inline uint8_t write_lcb(char *p, uint64_t length, bool nul = false) -{ - if (nul) - { - sw_mysql_int1store(p++, 251); - return 1; - } - if (length <= 250) - { - sw_mysql_int1store(p, length); - return 1; - } - else if (length <= 0xffff) - { - sw_mysql_int1store(p++, 252); - sw_mysql_int2store(p, length); - return 3; - } - else if (length <= 0xffffff) - { - sw_mysql_int1store(p++, 253); - sw_mysql_int3store(p, length); - return 4; - } - else - { - sw_mysql_int1store(p++, 254); - sw_mysql_int8store(p, length); - return 9; - } -} - -class packet -{ -public: - static inline uint32_t get_length(const char *data) - { - return sw_mysql_uint2korr3korr(data); - } - static inline uint32_t get_number(const char *data) - { - return (uint8_t) data[3]; - } - static inline void set_length(char *buffer, uint32_t length) - { - buffer[0] = length; - buffer[1] = length >> 8; - buffer[2] = length >> 16; - } - static inline void set_number(char *buffer, uint8_t number) - { - buffer[3] = number; - } - static inline void set_header(char *buffer, uint32_t length, uint8_t number) - { - set_length(buffer, length); - set_number(buffer, number); - } -}; - -class server_packet : public packet -{ -public: - struct header { - uint32_t length :24; - uint32_t number :8; - header() : length(0), number(0) { } - } header; - server_packet() { } - server_packet(const char *data) - { - parse(data); - } - inline void parse(const char *data) - { - header.length = packet::get_length(data); - header.number = packet::get_number(data); - } - static inline uint8_t parse_type(const char *data) - { - if (sw_unlikely(!data)) - { - return SW_MYSQL_PACKET_NULL; - } - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE]; - } - static inline bool is_eof(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_EOF; - } - static inline bool is_ok(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_OK; - } - static inline bool is_err(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_ERR; - } -}; - -class server_status -{ -public: - int16_t status = 0; - void operator =(uint16_t status) - { - this->status = status; - } - inline bool more_results_exists() - { - bool b = !!(status & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "More results exist = %u", b); - return b; - } -}; - -class client_packet : public packet -{ -public: - client_packet(size_t body_size = 1024 - SW_MYSQL_PACKET_HEADER_SIZE) - { - SW_ASSERT(body_size > 0); - if (body_size <= 4) - { - data.header = stack_buffer; - } - else - { - data.header = new char[SW_MEM_ALIGNED_SIZE(SW_MYSQL_PACKET_HEADER_SIZE + body_size)](); - } - data.body = data.header + SW_MYSQL_PACKET_HEADER_SIZE; - } - inline const char* get_data() - { - return data.header; - } - inline uint32_t get_data_length() - { - return SW_MYSQL_PACKET_HEADER_SIZE + get_length(); - } - inline uint32_t get_length() - { - return sw_mysql_uint2korr3korr(data.header); - } - inline uint8_t get_number() - { - return (uint8_t) data.header[3]; - } - inline const char* get_body() - { - return data.body; - } - inline void set_header(uint32_t length, uint8_t number) - { - packet::set_header(data.header, length, number); - } - ~client_packet() - { - if (data.header != stack_buffer) - { - delete[] data.header; - } - } -protected: - struct { - char *header = nullptr; - char *body = nullptr; - } data; - char stack_buffer[SW_MYSQL_PACKET_HEADER_SIZE + 4] = {}; -}; - -class command_packet : public client_packet -{ -public: - command_packet(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0) : client_packet(1 + length) - { - set_command(command); - set_header(1 + length, 0); - if (length > 0) - { - memcpy(data.body + 1, sql, length); - } - }; - inline void set_command(enum sw_mysql_command command) - { - data.body[0] = (char) command; - } -}; - -class err_packet : public server_packet -{ -public: - uint16_t code; - std::string msg; - char sql_state[5 + 1]; - err_packet(const char *data); -}; - -class ok_packet : public server_packet -{ -public: - uint64_t affected_rows = 0; - uint64_t last_insert_id = 0; - mysql::server_status server_status; - unsigned int warning_count = 0; - ok_packet() { } - ok_packet(const char *data); -}; - -class eof_packet : public server_packet -{ -public: - uint16_t warning_count; - mysql::server_status server_status; - eof_packet(const char *data); -}; - -class raw_data_packet : public server_packet -{ -public: - const char *body; - raw_data_packet(const char *data) : server_packet(data), body(data + SW_MYSQL_PACKET_HEADER_SIZE) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::RawData"); - } -}; - -class greeting_packet : public server_packet -{ -public: - uint8_t protocol_version = 0; - std::string server_version = ""; - int connection_id = 0; - char auth_plugin_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; // nonce + '\0' - uint8_t auth_plugin_data_length = 0; - char filler = 0; - int capability_flags = 0; - char charset = SW_MYSQL_DEFAULT_CHARSET; - mysql::server_status status_flags; - char reserved[10] = {}; - std::string auth_plugin_name = ""; - greeting_packet(const char *data); -}; - -class login_packet : public client_packet -{ -public: - login_packet( - greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset - ); -}; - -class auth_switch_request_packet : public server_packet -{ -public: - std::string auth_method_name = "mysql_native_password"; - char auth_method_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; - auth_switch_request_packet(const char *data); -}; - -class auth_switch_response_packet : public client_packet -{ -public: - auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password); -}; - -class auth_signature_request_packet : public server_packet -{ -public: - char data[2] = {}; - auth_signature_request_packet(const char *data) :server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSignatureRequest"); - memcpy(&this->data, data + SW_MYSQL_PACKET_HEADER_SIZE, 2); - } - inline bool is_full_auth_required() - { - return data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED; - } - inline bool is_vaild() - { - return data[0] == SW_MYSQL_AUTH_SIGNATURE && (data[1] == SW_MYSQL_AUTH_SIGNATURE_SUCCESS || data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED); - } -}; - -class auth_signature_prepared_packet : public client_packet -{ -public: - auth_signature_prepared_packet(uint8_t number) : client_packet(1) - { - set_header(1, number); - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED; - } -}; - -class auth_signature_response_packet : public client_packet -{ -public: - auth_signature_response_packet(raw_data_packet *raw_data_pakcet, const std::string &password, const char *auth_plugin_data); -}; - -class lcb_packet : public server_packet -{ -public: - uint32_t length = 0; - bool nul = 0; - lcb_packet(const char *data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::LengthCodedBinary"); - bytes_length = read_lcb(data + SW_MYSQL_PACKET_HEADER_SIZE, &length, &nul); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "binary_length=%u, nul=%u", header.length, nul); - } - bool is_vaild() - { - return header.length == bytes_length; - } -private: - uint8_t bytes_length; -}; - -class field_packet : public server_packet -{ -public: - char *catalog = nullptr; /* Catalog for table */ - uint32_t catalog_length = 0; - char *database = nullptr; /* Database for table */ - uint32_t database_length = 0; - char *table = nullptr; /* Table of column if column was a field */ - uint32_t table_length = 0; - char *org_table = nullptr; /* Org table name, if table was an alias */ - uint32_t org_table_length = 0; - char *name = nullptr; /* Name of column */ - uint32_t name_length = 0; - char *org_name = nullptr; /* Original column name, if an alias */ - uint32_t org_name_length = 0; - char charset = 0; - uint64_t length = 0; /* Width of column (create length) */ - uint8_t type = 0; /* Type of field. See mysql_com.h for types */ - uint32_t flags = 0; /* Div flags */ - uint32_t decimals = 0; /* Number of decimals in field */ - char *def = nullptr; /* Default value (set by mysql_list_fields) */ - uint32_t def_length = 0; - void *extension = nullptr; - field_packet() { } - field_packet(const char *data) { - parse(data); - } - void parse(const char *data); - ~field_packet() - { - if (body) - { - delete[] body; - } - } -protected: - char *body = nullptr; -}; - -typedef field_packet param_packet; - -class row_data -{ -public: - char stack_buffer[32]; - struct { - uint64_t length; // binary code length - bool nul; // is nul? - } text; - row_data(const char *data) - { - next_packet(data); - } - inline void next_packet(const char *data) - { - read_ptr = packet_body = data + SW_MYSQL_PACKET_HEADER_SIZE; - packet_eof = packet_body + packet::get_length(data); - } - inline bool eof() - { - return read_ptr == packet_eof; - } - inline const char* read(size_t length) - { - if (sw_likely(read_ptr + length <= packet_eof)) - { - const char *p = read_ptr; - read_ptr += length; - return p; - } - return nullptr; - } - inline uint32_t recv(char *buf, size_t size) - { - uint32_t readable_length = packet_eof - read_ptr; - uint32_t read_bytes = SW_MIN(readable_length, size); - if (sw_likely(read_bytes > 0)) - { - memcpy(buf, read_ptr, read_bytes); - read_ptr += read_bytes; - } - return read_bytes; - } -protected: - const char *packet_body; - const char *packet_eof; - const char *read_ptr; -}; - -class row_data_text -{ -public: - uint64_t length = 0; - bool nul = false; - const char *body = nullptr; - row_data_text(const char **pp) - { - body = *pp + read_lcb(*pp, &length, &nul); - *pp = body + length; - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, - "text[%" PRIu64 "]: %.*s%s", - length, (int) SW_MIN(64, length), body, - nul ? "null" : ((length > 64 /*|| length > readable_length*/) ? "..." : "") - ); - } -}; - -inline std::string datetime(const char *p, uint8_t length, uint32_t decimals) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0, h = 0, i = 0, s = 0; - uint32_t sp = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - if (length > 4) - { - h = *(uint8_t *) (p + 4); - i = *(uint8_t *) (p + 5); - s = *(uint8_t *) (p + 6); - } - if (length > 7) - { - sp = sw_mysql_uint2korr4korr(p + 7); - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u.%0*u", - y, m, d, h, i, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u", - y, m, d, h, i, s - ); - } -} - -inline std::string time(const char *p, uint8_t length, uint32_t decimals) -{ - bool neg = false; - uint32_t d = 0, sp = 0; - uint8_t h = 0, m = 0, s = 0; - if (length != 0) - { - neg = (bool) *((uint8_t *) p); - d = sw_mysql_uint2korr4korr(p + 1); - h = *(uint8_t *) (p + 5); - m = *(uint8_t *) (p + 6); - s = *(uint8_t *) (p + 7); - if (length > 8) - { - sp = sw_mysql_uint2korr4korr(p + 8); - } - if (d != 0) { - /* Convert days to hours at once */ - h += d * 24; - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%s%02u:%02u:%02u.%0*u", - (neg ? "-" : ""), h, m, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%s%02u:%02u:%02u", - (neg ? "-" : ""), h, m, s - ); - } -} - -inline std::string date(const char *p, uint8_t length) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - } - return swoole::std_string::format("%04u-%02u-%02u", y, m, d); -} - -class result_info -{ -public: - ok_packet ok; - - inline void alloc_fields(uint32_t length) - { - clear_fields(); - if (sw_likely(length != 0)) - { - fields.info = new field_packet[length]; - fields.length = length; - } - else - { - fields.length = 0; - fields.info = nullptr; - } - } - inline uint32_t get_fields_length() - { - return fields.length; - } - inline field_packet* get_fields(uint32_t index) - { - return fields.info; - } - inline field_packet* get_field(uint32_t index) - { - return &fields.info[index]; - } - inline void set_field(uint32_t index, const char *data) - { - fields.info[index].parse(data); - } - inline void clear_fields() - { - if (fields.length > 0) - { - delete[] fields.info; - } - } - ~result_info() - { - clear_fields(); - } -protected: - struct { - uint32_t length = 0; - field_packet *info = nullptr; - } fields; -}; - -class statement : public server_packet -{ -public: - uint32_t id = 0; - uint16_t field_count = 0; - uint16_t param_count = 0; - uint16_t warning_count = 0; - statement() { } - statement(const char* data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "COM_STMT_PREPARE_OK_Packet"); - // skip the packet header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // status (1) -- [00] OK - SW_ASSERT(data[0] == SW_MYSQL_PACKET_OK); - data += 1; - // statement_id (4) -- statement-id - id = sw_mysql_uint2korr4korr(data); - data += 4; - // num_columns (2) -- number of columns - field_count = sw_mysql_uint2korr2korr(data); - data += 2; - // num_params (2) -- number of params - param_count = sw_mysql_uint2korr2korr(data); - data += 2; - // reserved_1 (1) -- [00] filler - data += 1; - // warning_count (2) -- number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "statement_id=%u, field_count=%u, param_count=%u, warning_count=%u", - id, field_count, param_count, warning_count - ); - } -}; - -class null_bitmap -{ -public: - static uint32_t get_size(uint32_t field_length) - { - return ((field_length + 9) / 8) + 1; - } - null_bitmap(const char *p, uint32_t size) : - size(size) - { - map = new char[size]; - memcpy(map, p, size); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "null_count=%u", size); - } - inline bool is_null(size_t i) - { - return ((map + 1)[((i + 2) / 8)] & (0x01 << ((i + 2) % 8))) != 0; - } - ~null_bitmap() - { - delete[] map; - } -protected: - uint32_t size; - char *map; -}; -//-----------------------------------namespace end-------------------------------------------- -}} diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h new file mode 100644 index 00000000000..59e19c0f57b --- /dev/null +++ b/ext-src/php_swoole_odbc.h @@ -0,0 +1,129 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_ODBC_H +#define PHP_SWOOLE_ODBC_H + +#include "php_swoole.h" + +#ifdef SW_USE_ODBC +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h" +#else +#include "thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_odbc_driver; + +#include "php_version.h" +#define PHP_PDO_ODBC_VERSION PHP_VERSION + +RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, + SQLSMALLINT NameLength1, + SQLCHAR *UserName, + SQLSMALLINT NameLength2, + SQLCHAR *Authentication, + SQLSMALLINT NameLength3); + +SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion); + +SQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLength); + +SQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind); + +SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind); + +SQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount); + +SQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable); + +SQLRETURN SQL_API swoole_odbc_SQLMoreResults( + SQLHSTMT hstmt); + +SQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType); + +SQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle); + +SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle); + +void swoole_odbc_set_blocking(bool blocking); + +#ifdef SW_USE_ODBC_HOOK + +#define SQLConnect swoole_odbc_SQLConnect +#define SQLDriverConnect swoole_odbc_SQLDriverConnect +#define SQLExecDirect swoole_odbc_SQLExecDirect +#define SQLGetInfo swoole_odbc_SQLGetInfo +#define SQLGetDiagRec swoole_odbc_SQLGetDiagRec +#define SQLPrepare swoole_odbc_SQLPrepare +#define SQLExecute swoole_odbc_SQLExecute +#define SQLCloseCursor swoole_odbc_SQLCloseCursor +#define SQLGetData swoole_odbc_SQLGetData +#define SQLPutData swoole_odbc_SQLPutData +#define SQLRowCount swoole_odbc_SQLRowCount +#define SQLDescribeCol swoole_odbc_SQLDescribeCol +#define SQLEndTran swoole_odbc_SQLEndTran +#define SQLFreeHandle swoole_odbc_SQLFreeHandle +#define SQLDisconnect swoole_odbc_SQLDisconnect + +#endif +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h new file mode 100644 index 00000000000..7ce78a99e3f --- /dev/null +++ b/ext-src/php_swoole_oracle.h @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_ORACLE_H +#define PHP_SWOOLE_ORACLE_H +#include "php_swoole.h" + +#ifdef SW_USE_ORACLE + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_oci/php_pdo_oci_int.h" +#else +#include "thirdparty/php84/pdo_oci/php_pdo_oci_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_oci_driver; + +void swoole_oracle_set_blocking(bool blocking); +sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode); +sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode); +sword swoole_oci_stmt_prepare( + OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode); +sword swoole_oci_stmt_execute(OCISvcCtx *svchp, + OCIStmt *stmtp, + OCIError *errhp, + ub4 iters, + ub4 rowoff, + const OCISnapshot *snap_in, + OCISnapshot *snap_out, + ub4 mode); +sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode); +sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode); +sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags); +sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags); +sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode); + +#ifdef SW_USE_ORACLE_HOOK +#define OCISessionBegin swoole_oci_session_begin +#define OCIServerDetach swoole_oci_server_detach +#define OCIStmtPrepare swoole_oci_stmt_prepare +#define OCIStmtExecute swoole_oci_stmt_execute +#define OCIStmtFetch swoole_oci_stmt_fetch +#define OCIStmtFetch2 swoole_oci_stmt_fetch2 +#define OCITransCommit swoole_oci_trans_commit +#define OCITransRollback swoole_oci_trans_rollback +#define OCIPing swoole_oci_ping +#endif + +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/php_swoole_pgsql.h b/ext-src/php_swoole_pgsql.h new file mode 100644 index 00000000000..187c67d22af --- /dev/null +++ b/ext-src/php_swoole_pgsql.h @@ -0,0 +1,67 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_PGSQL_H +#define PHP_SWOOLE_PGSQL_H + +#include "php_swoole.h" + +#ifdef SW_USE_PGSQL + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h" +#else +#include "thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h" +#endif + + +extern const pdo_driver_t swoole_pdo_pgsql_driver; + +#include +#include + +void swoole_pgsql_set_blocking(bool blocking); + +PGconn *swoole_pgsql_connectdb(const char *conninfo); +PGresult *swoole_pgsql_prepare(PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types); +PGresult *swoole_pgsql_exec_prepared(PGconn *conn, const char *stmt_name, int n_params, + const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format); +PGresult *swoole_pgsql_exec(PGconn *conn, const char *query); +PGresult *swoole_pgsql_exec_params(PGconn *conn, const char *command, int n_params, + const Oid *param_types, const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format); + +#ifdef SW_USE_PGSQL_HOOK +#define PQconnectdb swoole_pgsql_connectdb +#define PQprepare swoole_pgsql_prepare +#define PQexecPrepared swoole_pgsql_exec_prepared +#define PQexec swoole_pgsql_exec +#define PQexecParams swoole_pgsql_exec_params +#endif + +END_EXTERN_C() + +#endif +#endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index cf7c8e7063d..049c898e8c9 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,6 +26,7 @@ #define SW_HAVE_COUNTABLE 1 +#include "swoole_c_api.h" #include "swoole_api.h" #include "swoole_async.h" @@ -61,29 +62,53 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); } else { \ RETURN_TRUE; \ } -#define SW_LOCK_CHECK_RETURN(s) \ - zend_long ___tmp_return_value = s; \ + +#define SW_LOCK_CHECK_RETURN(s) \ + zend_long ___tmp_return_value = s; \ if (___tmp_return_value == 0) { \ RETURN_TRUE; \ } else { \ - zend_update_property_long(NULL, SW_Z8_OBJ_P(ZEND_THIS), SW_STRL("errCode"), ___tmp_return_value ); \ + zend_update_property_long(NULL, SW_Z8_OBJ_P(ZEND_THIS), SW_STRL("errCode"), ___tmp_return_value); \ RETURN_FALSE; \ } +#ifdef SW_THREAD +#define SW_MUST_BE_MAIN_THREAD_EX(op) \ + if (!tsrm_is_main_thread()) { \ + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); \ + op; \ + } +#define SW_MUST_BE_MAIN_THREAD() SW_MUST_BE_MAIN_THREAD_EX(RETURN_TRUE) +#else +#define SW_MUST_BE_MAIN_THREAD_EX(op) +#define SW_MUST_BE_MAIN_THREAD() +#endif + #define php_swoole_fatal_error(level, fmt_str, ...) \ + swoole_set_last_error(SW_ERROR_PHP_FATAL_ERROR); \ php_error_docref(NULL, level, (const char *) (fmt_str), ##__VA_ARGS__) +/** + * The error occurred at the PHP layer and no error code was set + */ #define php_swoole_error(level, fmt_str, ...) \ - if (SWOOLE_G(display_errors) || level == E_ERROR) php_swoole_fatal_error(level, fmt_str, ##__VA_ARGS__) + swoole_set_last_error(SW_ERROR_PHP_RUNTIME_NOTICE); \ + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) -#define php_swoole_sys_error(level, fmt_str, ...) \ - php_swoole_error(level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) +/** + * The error occurred in the core must have error code + */ +#define php_swoole_core_error(level, fmt_str, ...) \ + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) -#ifdef SW_USE_OPENSSL -#ifndef HAVE_OPENSSL -#error "Enable openssl support, require openssl library" -#endif -#endif +#define php_swoole_error_ex(level, err_code, fmt_str, ...) \ + swoole_set_last_error(err_code); \ + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) + +#define php_swoole_sys_error(level, fmt_str, ...) \ + swoole_set_last_error(errno); \ + if (SWOOLE_G(display_errors) || level == E_ERROR) \ + php_error_docref(NULL, level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) #ifdef SW_USE_CARES #ifndef HAVE_CARES @@ -91,19 +116,35 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #endif #endif +#if defined(SW_HAVE_ZLIB) || defined(SW_HAVE_BROTLI) || defined(SW_HAVE_ZSTD) +#define SW_HAVE_COMPRESSION +#endif + #ifdef SW_SOCKETS #include "ext/sockets/php_sockets.h" #define SWOOLE_SOCKETS_SUPPORT #endif -#if PHP_VERSION_ID < 70200 -#error "require PHP version 7.2 or later" +#if PHP_VERSION_ID < 80100 +#error "require PHP version 8.1 or later" +#endif + +#if PHP_VERSION_ID > 80500 +#error "require PHP version 8.4 or earlier" #endif #if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT) #error "thread context cannot be used with ZTS" #endif +#if defined(SW_USE_IOURING) && !defined(__linux__) +#error "only linux support iouring" +#endif + +#if defined(SW_THREAD) && !defined(ZTS) +#error "swoole thread must be used with ZTS" +#endif + //-------------------------------------------------------- #define SW_MAX_FIND_COUNT 100 // for swoole_server::connection_list #define SW_PHP_CLIENT_BUFFER_SIZE 65535 @@ -146,8 +187,15 @@ enum php_swoole_req_status { PHP_SWOOLE_RSHUTDOWN_END, }; //--------------------------------------------------------- +enum php_swoole_hook_type { + PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK = SW_GLOBAL_HOOK_USER, + PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, + PHP_SWOOLE_HOOK_BEFORE_REQUEST, + PHP_SWOOLE_HOOK_AFTER_RESPONSE, +}; +//--------------------------------------------------------- -static sw_inline enum swSocketType php_swoole_socktype(long type) { +static sw_inline enum swSocketType php_swoole_get_socket_type(long type) { return (enum swSocketType)(type & (~SW_FLAG_SYNC) & (~SW_FLAG_ASYNC) & (~SW_FLAG_KEEP) & (~SW_SOCK_SSL)); } @@ -155,6 +203,7 @@ extern zend_class_entry *swoole_event_ce; extern zend_class_entry *swoole_timer_ce; extern zend_class_entry *swoole_socket_coro_ce; extern zend_class_entry *swoole_client_ce; +extern zend_object_handlers swoole_client_handlers; extern zend_class_entry *swoole_server_ce; extern zend_object_handlers swoole_server_handlers; extern zend_class_entry *swoole_redis_server_ce; @@ -169,6 +218,8 @@ extern zend_class_entry *swoole_server_port_ce; extern zend_class_entry *swoole_exception_ce; extern zend_object_handlers swoole_exception_handlers; extern zend_class_entry *swoole_error_ce; +extern zend_class_entry *swoole_resolve_context_ce; +extern zend_object_handlers swoole_resolve_context_handlers; PHP_FUNCTION(swoole_clear_dns_cache); PHP_FUNCTION(swoole_last_error); @@ -207,6 +258,7 @@ void php_swoole_event_minit(int module_number); // base void php_swoole_atomic_minit(int module_number); void php_swoole_lock_minit(int module_number); +int swoole_resolve_context_module_init(INIT_FUNC_ARGS); void php_swoole_process_minit(int module_number); void php_swoole_process_pool_minit(int module_number); void php_swoole_table_minit(int module_number); @@ -215,44 +267,78 @@ void php_swoole_timer_minit(int module_number); void php_swoole_coroutine_minit(int module_number); void php_swoole_coroutine_system_minit(int module_number); void php_swoole_coroutine_scheduler_minit(int module_number); +void php_swoole_coroutine_lock_minit(int module_number); void php_swoole_channel_coro_minit(int module_number); void php_swoole_runtime_minit(int module_number); // client void php_swoole_socket_coro_minit(int module_number); void php_swoole_client_minit(int module_number); +void php_swoole_client_async_minit(int module_number); void php_swoole_client_coro_minit(int module_number); void php_swoole_http_client_coro_minit(int module_number); -void php_swoole_mysql_coro_minit(int module_number); -void php_swoole_redis_coro_minit(int module_number); -#ifdef SW_USE_HTTP2 void php_swoole_http2_client_coro_minit(int module_number); +#ifdef SW_USE_PGSQL +void php_swoole_pgsql_minit(int module_number); +#endif +#ifdef SW_USE_ODBC +int php_swoole_odbc_minit(int module_id); +#endif +#ifdef SW_USE_ORACLE +void php_swoole_oracle_minit(int module_number); +#endif +#ifdef SW_USE_SQLITE +void php_swoole_sqlite_minit(int module_number); #endif // server void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); void php_swoole_http_request_minit(int module_number); void php_swoole_http_response_minit(int module_number); +void php_swoole_http_cookie_minit(int module_number); void php_swoole_http_server_minit(int module_number); void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); void php_swoole_redis_server_minit(int module_number); +void php_swoole_name_resolver_minit(int module_number); +#ifdef SW_THREAD +void php_swoole_thread_minit(int module_number); +void php_swoole_thread_atomic_minit(int module_number); +void php_swoole_thread_lock_minit(int module_number); +void php_swoole_thread_barrier_minit(int module_number); +void php_swoole_thread_queue_minit(int module_number); +void php_swoole_thread_map_minit(int module_number); +void php_swoole_thread_arraylist_minit(int module_number); +#endif /** * RINIT * ============================================================== */ +void php_swoole_http_server_rinit(); +void php_swoole_websocket_server_rinit(); void php_swoole_coroutine_rinit(); void php_swoole_runtime_rinit(); +#ifdef SW_USE_ORACLE +void php_swoole_oracle_rinit(); +#endif +void php_swoole_thread_rinit(); /** * RSHUTDOWN * ============================================================== */ +void php_swoole_http_server_rshutdown(); +void php_swoole_websocket_server_rshutdown(); void php_swoole_async_coro_rshutdown(); void php_swoole_redis_server_rshutdown(); void php_swoole_coroutine_rshutdown(); +void php_swoole_process_rshutdown(); +void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); +#ifdef SW_THREAD +void php_swoole_thread_rshutdown(); +#endif int php_swoole_reactor_init(); void php_swoole_set_global_option(zend_array *vht); @@ -273,6 +359,16 @@ void php_swoole_event_exit(); * ============================================================== */ void php_swoole_runtime_mshutdown(); +void php_swoole_websocket_server_mshutdown(); +#ifdef SW_USE_PGSQL +void php_swoole_pgsql_mshutdown(); +#endif +#ifdef SW_USE_ORACLE +void php_swoole_oracle_mshutdown(); +#endif +#ifdef SW_USE_SQLITE +void php_swoole_sqlite_mshutdown(); +#endif static sw_inline zend_bool php_swoole_websocket_frame_is_object(zval *zdata) { return Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce); @@ -291,121 +387,23 @@ int php_swoole_convert_to_fd_ex(zval *zsocket, int *async); php_socket *php_swoole_convert_to_socket(int sock); #endif -zend_bool php_swoole_signal_isset_handler(int signo); - -/* PHP 7 compatibility patches */ -#define sw_zend_bailout() zend_bailout() - -// Fixed in php-7.2.3RC1 (https://github.com/php/php-src/commit/e88e83d3e5c33fcd76f08b23e1a2e4e8dc98ce41) -#if PHP_MAJOR_VERSION == 7 && ((PHP_MINOR_VERSION == 2 && PHP_RELEASE_VERSION < 3)) -// See https://github.com/php/php-src/commit/0495bf5650995cd8f18d6a9909eb4c5dcefde669 -// Then https://github.com/php/php-src/commit/2dcfd8d16f5fa69582015cbd882aff833075a34c -// See https://github.com/php/php-src/commit/52db03b3e52bfc886896925d050af79bc4dc1ba3 -#if PHP_MINOR_VERSION == 2 -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR \ - zend_wrong_parameters_count_error(_flags &ZEND_PARSE_PARAMS_THROW, _num_args, _min_num_args, _max_num_args) -#else -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR zend_wrong_parameters_count_error(_num_args, _min_num_args, _max_num_args) -#endif - -#undef ZEND_PARSE_PARAMETERS_START_EX - -#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) \ - do { \ - const int _flags = (flags); \ - int _min_num_args = (min_num_args); \ - int _max_num_args = (max_num_args); \ - int _num_args = EX_NUM_ARGS(); \ - int _i; \ - zval *_real_arg, *_arg = NULL; \ - zend_expected_type _expected_type = Z_EXPECTED_LONG; \ - char *_error = NULL; \ - zend_bool _dummy; \ - zend_bool _optional = 0; \ - int error_code = ZPP_ERROR_OK; \ - ((void) _i); \ - ((void) _real_arg); \ - ((void) _arg); \ - ((void) _expected_type); \ - ((void) _error); \ - ((void) _dummy); \ - ((void) _optional); \ - \ - do { \ - if (UNEXPECTED(_num_args < _min_num_args) || \ - (UNEXPECTED(_num_args > _max_num_args) && EXPECTED(_max_num_args >= 0))) { \ - if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ - SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR; \ - } \ - error_code = ZPP_ERROR_FAILURE; \ - break; \ - } \ - _i = 0; \ - _real_arg = ZEND_CALL_ARG(execute_data, 0); +#ifdef HAVE_CPU_AFFINITY +bool php_swoole_array_to_cpu_set(zval *array, cpu_set_t *cpu_set); +void php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set); #endif -/* PHP 7.3 compatibility macro {{{*/ - -#ifndef GC_ADDREF -#define GC_ADDREF(ref) ++GC_REFCOUNT(ref) -#define GC_DELREF(ref) --GC_REFCOUNT(ref) -#endif - -#ifndef ZEND_CLOSURE_OBJECT -#define ZEND_CLOSURE_OBJECT(func) (zend_object *) func->op_array.prototype -#endif - -/* PHP 7.4 compatibility macro {{{*/ -#ifndef ZEND_COMPILE_EXTENDED_STMT -#define ZEND_COMPILE_EXTENDED_STMT ZEND_COMPILE_EXTENDED_INFO -#endif - -#ifndef ZVAL_EMPTY_ARRAY -#define ZVAL_EMPTY_ARRAY(zval) (array_init((zval))) -#endif -#ifndef RETVAL_EMPTY_ARRAY -#define RETVAL_EMPTY_ARRAY() ZVAL_EMPTY_ARRAY(return_value) -#endif -#ifndef RETURN_EMPTY_ARRAY -#define RETURN_EMPTY_ARRAY() \ - do { \ - RETVAL_EMPTY_ARRAY(); \ - return; \ - } while (0) -#endif - -#ifndef ZEND_THIS -#define ZEND_THIS (&EX(This)) -#endif - -#ifndef ZEND_THIS_OBJECT -#define ZEND_THIS_OBJECT Z_OBJ_P(ZEND_THIS) -#endif +zend_bool php_swoole_signal_isset_handler(int signo); -#ifndef E_FATAL_ERRORS -#define E_FATAL_ERRORS (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE) +#if PHP_VERSION_ID < 80200 +#define zend_atomic_bool zend_bool +#define zend_atomic_bool_store(atomic, desired) (*atomic = desired) #endif -/*}}}*/ -/* PHP 8 compatibility macro {{{*/ -#if PHP_VERSION_ID < 80000 -#define sw_zend7_object zval -#define SW_Z7_OBJ_P(object) Z_OBJ_P(object) -#define SW_Z8_OBJ_P(zobj) zobj -#else #define sw_zend7_object zend_object -#define SW_Z7_OBJ_P(object) object #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) -#endif -/*}}}*/ -#if PHP_VERSION_ID < 70400 -typedef size_t php_stream_size_t; -#else typedef ssize_t php_stream_size_t; -#endif - -/* PHP 7 wrapper functions / macros */ +typedef zend_string error_filename_t; //----------------------------------Zval API------------------------------------ @@ -417,18 +415,8 @@ typedef ssize_t php_stream_size_t; (ptr) = &(val); \ } while (0) -#if PHP_VERSION_ID < 80000 -#define SW_ZVAL_SOCKET(return_value, result) \ - ZVAL_RES(return_value, zend_register_resource((void *) (result), php_sockets_le_socket())) -#else #define SW_ZVAL_SOCKET(return_value, result) ZVAL_OBJ(return_value, &result->std) -#endif - -#if PHP_VERSION_ID < 80000 -#define SW_Z_SOCKET_P(zsocket) (php_socket *) zend_fetch_resource_ex(zsocket, nullptr, php_sockets_le_socket()) -#else #define SW_Z_SOCKET_P(zsocket) Z_SOCKET_P(zsocket) -#endif #ifndef ZVAL_IS_BOOL static sw_inline zend_bool ZVAL_IS_BOOL(zval *v) { @@ -442,6 +430,12 @@ static sw_inline zend_bool ZVAL_IS_TRUE(zval *v) { } #endif +#ifndef ZVAL_IS_UNDEF +static sw_inline zend_bool ZVAL_IS_UNDEF(zval *v) { + return Z_TYPE_P(v) == IS_UNDEF; +} +#endif + #ifndef ZVAL_IS_FALSE static sw_inline zend_bool ZVAL_IS_FALSE(zval *v) { return Z_TYPE_P(v) == IS_FALSE; @@ -493,6 +487,50 @@ static sw_inline void sw_zval_free(zval *val) { efree(val); } +#ifdef SWOOLE_SOCKETS_SUPPORT +static inline bool sw_zval_is_php_socket(zval *val) { + return instanceof_function(Z_OBJCE_P(val), socket_ce); +} +#endif + +static inline bool sw_zval_is_co_socket(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_socket_coro_ce); +} + +static inline bool sw_zval_is_client(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_client_ce); +} + +static inline bool sw_zval_is_process(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_process_ce); +} + +bool sw_zval_is_serializable(zval *struc); + +static inline bool sw_is_main_thread() { +#ifdef SW_THREAD + return tsrm_is_main_thread(); +#else + return true; +#endif +} + +#ifdef SW_THREAD +size_t sw_active_thread_count(void); +#else +static inline size_t sw_active_thread_count(void) { + return 1; +} +#endif + +zend_refcounted *sw_get_refcount_ptr(zval *value); + +void sw_php_exit(int status); +void sw_php_print_backtrace(zend_long cid = 0, + zend_long options = 0, + zend_long limit = 0, + zval *return_value = nullptr); + //----------------------------------Constant API------------------------------------ #define SW_REGISTER_NULL_CONSTANT(name) REGISTER_NULL_CONSTANT(name, CONST_CS | CONST_PERSISTENT) @@ -523,7 +561,7 @@ static sw_inline void sw_zval_free(zval *val) { static sw_inline zend_string *sw_zend_string_recycle(zend_string *s, size_t alloc_len, size_t real_len) { SW_ASSERT(!ZSTR_IS_INTERNED(s)); if (UNEXPECTED(alloc_len != real_len)) { - if (alloc_len > SwooleG.pagesize && alloc_len > real_len * 2) { + if (alloc_len > swoole_pagesize() && alloc_len > real_len * 2) { s = zend_string_realloc(s, real_len, 0); } else { ZSTR_LEN(s) = real_len; @@ -589,31 +627,33 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon /* PHP 7 class declaration macros */ -#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_ce) \ +#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_ce) \ do { \ zend_class_entry _##module##_ce = {}; \ INIT_CLASS_ENTRY(_##module##_ce, namespace_name, methods); \ module##_ce = zend_register_internal_class_ex(&_##module##_ce, parent_ce); \ - if (snake_name) SW_CLASS_ALIAS(snake_name, module); \ if (short_name) SW_CLASS_ALIAS_SHORT_NAME(short_name, module); \ } while (0) -#define SW_INIT_CLASS_ENTRY(module, namespace_name, snake_name, short_name, methods) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, NULL); \ +#define SW_INIT_CLASS_ENTRY_STD(module, namespace_name, methods) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, nullptr, methods, NULL); \ + memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) + +#define SW_INIT_CLASS_ENTRY(module, namespace_name, short_name, methods) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) -#define SW_INIT_CLASS_ENTRY_EX(module, namespace_name, snake_name, short_name, methods, parent_module) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_module##_ce); \ +#define SW_INIT_CLASS_ENTRY_EX(module, namespace_name, short_name, methods, parent_module) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module##_ce); \ memcpy(&module##_handlers, &parent_module##_handlers, sizeof(zend_object_handlers)) -#define SW_INIT_CLASS_ENTRY_EX2( \ - module, namespace_name, snake_name, short_name, methods, parent_module_ce, parent_module_handlers) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_module_ce); \ +#define SW_INIT_CLASS_ENTRY_EX2(module, namespace_name, short_name, methods, parent_module_ce, parent_module_handlers) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module_ce); \ memcpy(&module##_handlers, parent_module_handlers, sizeof(zend_object_handlers)) // Data Object: no methods, no parent #define SW_INIT_CLASS_ENTRY_DATA_OBJECT(module, namespace_name) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, NULL, NULL, NULL, NULL); \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, NULL, NULL, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) #define SW_CLASS_ALIAS(name, module) \ @@ -632,11 +672,10 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon #if PHP_VERSION_ID < 80100 #define SW_SET_CLASS_NOT_SERIALIZABLE(module) \ - module##_ce->serialize = zend_class_serialize_deny; \ + module##_ce->serialize = zend_class_serialize_deny; \ module##_ce->unserialize = zend_class_unserialize_deny; #else -#define SW_SET_CLASS_NOT_SERIALIZABLE(module) \ - module##_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; +#define SW_SET_CLASS_NOT_SERIALIZABLE(module) module##_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; #endif #define sw_zend_class_clone_deny NULL @@ -665,15 +704,17 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon } \ } while (0) -#define SW_FUNCTION_ALIAS(origin_function_table, origin, alias_function_table, alias) \ - sw_zend_register_function_alias(origin_function_table, ZEND_STRL(origin), alias_function_table, ZEND_STRL(alias)) +#define SW_FUNCTION_ALIAS(origin_function_table, origin, alias_function_table, alias, arg_info) \ + sw_zend_register_function_alias( \ + origin_function_table, ZEND_STRL(origin), alias_function_table, ZEND_STRL(alias), arg_info) static sw_inline int sw_zend_register_function_alias(zend_array *origin_function_table, const char *origin, size_t origin_length, zend_array *alias_function_table, const char *alias, - size_t alias_length) { + size_t alias_length, + const zend_internal_arg_info *arg_info) { zend_string *lowercase_origin = zend_string_alloc(origin_length, 0); zend_str_tolower_copy(ZSTR_VAL(lowercase_origin), origin, origin_length); zend_function *origin_function = (zend_function *) zend_hash_find_ptr(origin_function_table, lowercase_origin); @@ -684,14 +725,11 @@ static sw_inline int sw_zend_register_function_alias(zend_array *origin_function SW_ASSERT(origin_function->common.type == ZEND_INTERNAL_FUNCTION); char *_alias = (char *) emalloc(alias_length + 1); ((char *) memcpy(_alias, alias, alias_length))[alias_length] = '\0'; - zend_function_entry zfe[] = {{_alias, - origin_function->internal_function.handler, - ((zend_internal_arg_info *) origin_function->common.arg_info) - 1, - origin_function->common.num_args, - 0}, - PHP_FE_END}; - int ret = - zend_register_functions(origin_function->common.scope, zfe, alias_function_table, origin_function->common.type); + + zend_function_entry zfe[] = { + {_alias, origin_function->internal_function.handler, arg_info, origin_function->common.num_args, 0}, + PHP_FE_END}; + int ret = zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type); efree(_alias); return ret; } @@ -708,25 +746,9 @@ static sw_inline int sw_zend_register_class_alias(const char *name, size_t name_ zend_string *_interned_name = zend_new_interned_string(_name); -#if PHP_VERSION_ID >= 70300 return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce, 1); -#else - return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce); -#endif } -#if PHP_VERSION_ID < 70300 -/* Allocates object type and zeros it, but not the properties. - * Properties MUST be initialized using object_properties_init(). */ -static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_entry *ce) { - void *obj = emalloc(obj_size + zend_object_properties_size(ce)); - /* Subtraction of sizeof(zval) is necessary, because zend_object_properties_size() may be - * -sizeof(zval), if the object has no properties. */ - memset(obj, 0, obj_size - sizeof(zval)); - return obj; -} -#endif - static sw_inline zend_object *sw_zend_create_object(zend_class_entry *ce, zend_object_handlers *handlers) { zend_object *object = (zend_object *) zend_object_alloc(sizeof(zend_object), ce); zend_object_std_init(object, ce); @@ -751,21 +773,6 @@ static sw_inline zend_object *sw_zend_create_object_deny(zend_class_entry *ce) { return object; } -#if PHP_VERSION_ID < 80000 -static sw_inline void sw_zend_class_unset_property_deny(zval *zobject, zval *zmember, void **cache_slot) { - zend_class_entry *ce = Z_OBJCE_P(zobject); - while (ce->parent) { - ce = ce->parent; - } - SW_ASSERT(ce->type == ZEND_INTERNAL_CLASS); - if (EXPECTED(zend_hash_find(&ce->properties_info, Z_STR_P(zmember)))) { - zend_throw_error( - NULL, "Property %s of class %s cannot be unset", Z_STRVAL_P(zmember), SW_Z_OBJCE_NAME_VAL_P(zobject)); - return; - } - std_object_handlers.unset_property(zobject, zmember, cache_slot); -} -#else static sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zend_string *member, void **cache_slot) { zend_class_entry *ce = object->ce; while (ce->parent) { @@ -778,9 +785,8 @@ static sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zen } std_object_handlers.unset_property(object, member, cache_slot); } -#endif -static sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { +static sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); if (UNEXPECTED(property == &EG(uninitialized_zval))) { zend_update_property_null(ce, SW_Z8_OBJ_P(obj), s, len); @@ -796,17 +802,18 @@ static sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, z zend_update_property_ex(scope, SW_Z8_OBJ_P(object), s, &tmp); } -static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *obj, zend_string *s, int silent) { - zval rv, *property = zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv); +static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *zobject, zend_string *name, int silent) { + zval *zv = zend_hash_find(&ce->properties_info, name); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset); if (UNEXPECTED(property == &EG(uninitialized_zval))) { - sw_zend_update_property_null_ex(ce, obj, s); - return zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv); + ZVAL_NULL(property); } return property; } static sw_inline zval *sw_zend_read_property_not_null( - zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { + zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); zend_uchar type = Z_TYPE_P(property); return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? NULL : property; @@ -818,7 +825,10 @@ static sw_inline zval *sw_zend_read_property_not_null_ex(zend_class_entry *ce, z return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? NULL : property; } -static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, zval *obj, const char *s, int len) { +static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, + zval *obj, + const char *s, + size_t len) { zval ztmp; array_init(&ztmp); zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp); @@ -827,7 +837,7 @@ static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry * } static sw_inline zval *sw_zend_read_and_convert_property_array( - zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { + zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); if (Z_TYPE_P(property) != IS_ARRAY) { // NOTICE: if user unset the property, zend_read_property will return uninitialized_zval instead of NULL pointer @@ -889,14 +899,9 @@ static sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable, size_t *callable_name_len, zend_fcall_info_cache *fci_cache, char **error) { - zend_string *name; - zend_bool ret; -#if PHP_VERSION_ID < 80000 - ret = zend_is_callable_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, check_flags, &name, fci_cache, error); -#else - ret = zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); - name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL); -#endif + zend_bool ret = + zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); + zend_string *name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL); if (callable_name) { *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); } @@ -939,11 +944,7 @@ static sw_inline int sw_zend_call_function_ex( fci.retval = retval ? retval : &_retval; fci.param_count = param_count; fci.params = params; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif ret = zend_call_function(&fci, fci_cache); @@ -965,10 +966,6 @@ static sw_inline int sw_zend_call_function_ex2( static sw_inline int sw_zend_call_function_anyway(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) { zval retval; - zend_object *exception = EG(exception); - if (exception) { - EG(exception) = NULL; - } if (!fci->retval) { fci->retval = &retval; } @@ -976,9 +973,6 @@ static sw_inline int sw_zend_call_function_anyway(zend_fcall_info *fci, zend_fca if (fci->retval == &retval) { zval_ptr_dtor(&retval); } - if (exception) { - EG(exception) = exception; - } return ret; } @@ -1021,11 +1015,11 @@ static sw_inline void sw_zend_fci_cache_discard(zend_fcall_info_cache *fci_cache } } -/* use void* to match some C callback function pointers */ -static sw_inline void sw_zend_fci_cache_free(void *fci_cache) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) fci_cache); - efree((zend_fcall_info_cache *) fci_cache); -} +#if PHP_VERSION_ID >= 80100 +#define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o)) +#else +#define sw_php_spl_object_hash(o) php_spl_object_hash(o) +#endif //----------------------------------Misc API------------------------------------ @@ -1047,7 +1041,7 @@ static sw_inline char *php_swoole_format_date(char *format, size_t format_len, t return return_str; } -static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, int *exten) { +static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, size_t *exten) { zend_string *str = php_url_encode(value, value_len); *exten = ZSTR_LEN(str); char *return_str = estrndup(ZSTR_VAL(str), ZSTR_LEN(str)); @@ -1056,14 +1050,13 @@ static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len } static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) { -#if PHP_VERSION_ID < 80000 - if (php_url_encode_hash_ex( - HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738) == FAILURE) { -#else if (HASH_OF(zdata)) { +#if PHP_VERSION_ID < 80300 php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); - } else { +#else + php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, NULL, NULL, (int) PHP_QUERY_RFC1738); #endif + } else { if (formstr->s) { smart_str_free(formstr); } @@ -1077,6 +1070,18 @@ static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, return formstr->s->val; } +static inline const char *php_swoole_get_last_error_message() { + return PG(last_error_message) ? PG(last_error_message)->val : nullptr; +} + +static inline const char *php_swoole_get_last_error_file() { +#if PHP_VERSION_ID >= 80100 + return PG(last_error_file) ? PG(last_error_file)->val : "-"; +#else + return PG(last_error_file) ? PG(last_error_file) : "-"; +#endif +} + END_EXTERN_C() #endif /* PHP_SWOOLE_PRIVATE_H */ diff --git a/ext-src/php_swoole_process.h b/ext-src/php_swoole_process.h index 3704b69c896..7717ca7ead6 100644 --- a/ext-src/php_swoole_process.h +++ b/ext-src/php_swoole_process.h @@ -11,7 +11,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Twosee | - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,3 +24,5 @@ void php_swoole_process_clean(); int php_swoole_process_start(swoole::Worker *process, zval *zobject); swoole::Worker *php_swoole_process_get_worker(zval *zobject); void php_swoole_process_set_worker(zval *zobject, swoole::Worker *worker); + +swoole::ProcessPool *sw_process_pool(); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 97a8e7cce0c..0fe4904a0e0 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -44,29 +44,36 @@ enum php_swoole_server_callback_type { }; //-------------------------------------------------------- enum php_swoole_server_port_callback_type { - SW_SERVER_CB_onConnect, // stream, worker(event) - SW_SERVER_CB_onReceive, // stream, worker(event) - SW_SERVER_CB_onClose, // stream, worker(event) - SW_SERVER_CB_onPacket, // dgram, worker(event) - SW_SERVER_CB_onRequest, // http, worker(event) - SW_SERVER_CB_onHandShake, // websocket, worker(event) - SW_SERVER_CB_onOpen, // websocket, worker(event) - SW_SERVER_CB_onMessage, // websocket, worker(event) - SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) - SW_SERVER_CB_onBufferFull, // worker(event) - SW_SERVER_CB_onBufferEmpty, // worker(event) + SW_SERVER_CB_onConnect, // stream, worker(event) + SW_SERVER_CB_onReceive, // stream, worker(event) + SW_SERVER_CB_onClose, // stream, worker(event) + SW_SERVER_CB_onPacket, // dgram, worker(event) + SW_SERVER_CB_onRequest, // http, worker(event) + SW_SERVER_CB_onHandshake, // websocket, worker(event) + SW_SERVER_CB_onBeforeHandshakeResponse, // websocket, worker(event) + SW_SERVER_CB_onOpen, // websocket, worker(event) + SW_SERVER_CB_onMessage, // websocket, worker(event) + SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) + SW_SERVER_CB_onBufferFull, // worker(event) + SW_SERVER_CB_onBufferEmpty, // worker(event) }; #define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) #define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) namespace swoole { +struct ServerPortProperty; struct TaskCo; +}; // namespace swoole + +zval *php_swoole_server_zval_ptr(swoole::Server *serv); +swoole::ServerPortProperty *php_swoole_server_get_port_property(swoole::ListenPort *port); +void php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::ServerPortProperty *property); + +namespace swoole { struct ServerPortProperty { - zval *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; - zend_fcall_info_cache *caches[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; - zval _callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; + zend::Callable *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; Server *serv; ListenPort *port; zval *zsetting; @@ -75,30 +82,34 @@ struct ServerPortProperty { struct ServerProperty { std::vector ports; std::vector user_processes; - ServerPortProperty *primary_port; - zend_fcall_info_cache *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; - std::unordered_map task_callbacks; + zend::Callable *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; + std::unordered_map task_callbacks; std::unordered_map task_coroutine_map; std::unordered_map *> send_coroutine_map; - std::vector command_callbacks; + std::vector command_callbacks; }; struct ServerObject { Server *serv; ServerProperty *property; + zval init_arguments; zend_object std; zend_class_entry *get_ce() { - return Z_OBJCE_P(get_object()); + return Z_OBJCE_P(php_swoole_server_zval_ptr(serv)); } - zval *get_object() { - return (zval *) serv->private_data_2; + bool isset_callback(ListenPort *port, int event_type) { + return (php_swoole_server_get_port_property(port)->callbacks[event_type] || + php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]); } - bool isset_callback(ListenPort *port, int event_type) { - ServerPortProperty *port_property = (ServerPortProperty *) port->ptr; - return (port_property->callbacks[event_type] || property->primary_port->callbacks[event_type]); + bool isset_callback(int event_type) { + return property->callbacks[event_type] != nullptr; + } + + zend::Callable *get_callback(int event_type) { + return property->callbacks[event_type]; } zend_bool is_websocket_server() { @@ -115,28 +126,29 @@ struct ServerObject { void register_callback(); void on_before_start(); + void copy_setting(zval *zsetting); }; struct TaskCo { Coroutine *co; - int *list; + TaskId *list; uint32_t count; zval *result; }; - +void register_admin_server_commands(Server *serv); } // namespace swoole void php_swoole_server_register_callbacks(swServer *serv); -zend_fcall_info_cache *php_swoole_server_get_fci_cache(swServer *serv, int server_fd, int event_type); +zend::Callable *php_swoole_server_get_callback(swServer *serv, int server_fd, int event_type); int php_swoole_create_dir(const char *path, size_t length); void php_swoole_server_before_start(swServer *serv, zval *zobject); bool php_swoole_server_isset_callback(swServer *serv, swListenPort *port, int event_type); -void php_swoole_http_server_init_global_variant(); void php_swoole_server_send_yield(swServer *serv, swoole::SessionId sesion_id, zval *zdata, zval *return_value); void php_swoole_get_recv_data(swServer *serv, zval *zdata, swRecvData *req); void php_swoole_server_onConnect(swServer *, swDataHead *); int php_swoole_server_onReceive(swServer *, swRecvData *); int php_swoole_http_server_onReceive(swServer *, swRecvData *); +void php_swoole_http_server_onClose(swServer *serv, swDataHead *info); int php_swoole_redis_server_onReceive(swServer *serv, swRecvData *req); int php_swoole_server_onPacket(swServer *, swRecvData *); void php_swoole_server_onClose(swServer *, swDataHead *); @@ -146,4 +158,4 @@ void php_swoole_server_onBufferEmpty(swServer *, swDataHead *); swServer *php_swoole_server_get_and_check_server(zval *zobject); void php_swoole_server_port_deref(zend_object *object); swoole::ServerObject *php_swoole_server_get_zend_object(swoole::Server *serv); -zval *php_swoole_server_get_zval_object(swoole::Server *serv); + diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h new file mode 100644 index 00000000000..209a4ad5565 --- /dev/null +++ b/ext-src/php_swoole_sqlite.h @@ -0,0 +1,61 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ +#ifndef SWOOLE_SRC_PHP_SWOOLE_SQLITE_H +#define SWOOLE_SRC_PHP_SWOOLE_SQLITE_H +#include "php_swoole.h" + +#ifdef SW_USE_SQLITE + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h" +#else +#include "thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_sqlite_driver; +void swoole_sqlite_set_blocking(bool blocking); + +int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs); +int swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail); +int swoole_sqlite3_exec( + sqlite3 *, const char *sql, int (*callback)(void *, int, char **, char **), void *, char **errmsg); +int swoole_sqlite3_close(sqlite3 *db); +int swoole_sqlite3_close_v2(sqlite3 *db); +int swoole_sqlite3_step(sqlite3_stmt *stmt); + +#ifdef SW_USE_SQLITE_HOOK +#define sqlite3_open_v2 swoole_sqlite3_open_v2 +#define sqlite3_prepare_v2 swoole_sqlite3_prepare_v2 +#define sqlite3_exec swoole_sqlite3_exec +#define sqlite3_close swoole_sqlite3_close +#define sqlite3_close_v2 swoole_sqlite3_close_v2 +#define sqlite3_step swoole_sqlite3_step +#endif +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h new file mode 100644 index 00000000000..b1fe246f714 --- /dev/null +++ b/ext-src/php_swoole_thread.h @@ -0,0 +1,317 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Twosee | + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD + +#include "swoole_lock.h" + +typedef uint32_t ThreadResourceId; +class ThreadResource; +class ZendArray; + +extern zend_class_entry *swoole_thread_ce; +extern zend_class_entry *swoole_thread_error_ce; +extern zend_class_entry *swoole_thread_arraylist_ce; +extern zend_class_entry *swoole_thread_atomic_ce; +extern zend_class_entry *swoole_thread_atomic_long_ce; +extern zend_class_entry *swoole_thread_barrier_ce; +extern zend_class_entry *swoole_thread_lock_ce; +extern zend_class_entry *swoole_thread_map_ce; +extern zend_class_entry *swoole_thread_queue_ce; + +void php_swoole_thread_start(zend_string *file, ZendArray *argv); +void php_swoole_thread_join(pthread_t ptid); +int php_swoole_thread_get_exit_status(pthread_t ptid); +void php_swoole_thread_bailout(void); + +ThreadResource *php_swoole_thread_arraylist_cast(zval *zobject); +ThreadResource *php_swoole_thread_map_cast(zval *zobject); +ThreadResource *php_swoole_thread_queue_cast(zval *zobject); +ThreadResource *php_swoole_thread_lock_cast(zval *zobject); +ThreadResource *php_swoole_thread_atomic_cast(zval *zobject); +ThreadResource *php_swoole_thread_atomic_long_cast(zval *zobject); +ThreadResource *php_swoole_thread_barrier_cast(zval *zobject); + +void php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_map_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource); + +int php_swoole_thread_stream_cast(zval *zstream); +void php_swoole_thread_stream_create(zval *return_value, zend_long sockfd); + +int php_swoole_thread_co_socket_cast(zval *zstream, swSocketType *type); +void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type); + +#define EMSG_NO_RESOURCE "resource not found" +#define ECODE_NO_RESOURCE -2 + +enum { + IS_ARRAYLIST = 80, + IS_QUEUE = 81, + IS_LOCK = 82, + IS_MAP = 83, + IS_BARRIER = 84, + IS_ATOMIC = 85, + IS_ATOMIC_LONG = 86, + IS_PHP_SOCKET = 96, + IS_CO_SOCKET = 97, + IS_STREAM_SOCKET = 98, + IS_SERIALIZED_OBJECT = 99, +}; + +class ThreadResource { + sw_atomic_t ref_count; + + public: + ThreadResource() { + ref_count = 1; + } + + void add_ref() { + sw_atomic_add_fetch(&ref_count, 1); + } + + void del_ref() { + if (sw_atomic_sub_fetch(&ref_count, 1) == 0) { + delete this; + } + } + + protected: + virtual ~ThreadResource() {} +}; + +struct ArrayItem { + uint32_t type = IS_UNDEF; + zend_string *key = nullptr; + union { + zend_string *str; + zend_long lval; + double dval; + struct { + int fd; + swSocketType type; + } socket; + zend_string *serialized_object; + ThreadResource *resource; + } value; + + ArrayItem(zval *zvalue) { + value = {}; + store(zvalue); + } + + void setKey(zend::String &_key) { + key = zend_string_init(_key.val(), _key.len(), 1); + } + + void setKey(zend_string *_key) { + key = zend_string_init(ZSTR_VAL(_key), ZSTR_LEN(_key), 1); + } + + void store(zval *zvalue); + void fetch(zval *return_value); + void release(); + bool equals(zval *zvalue); + + static int compare(Bucket *a, Bucket *b); + + ~ArrayItem() { + if (value.str) { + release(); + } + if (key) { + zend_string_release(key); + } + } +}; + +class ZendArray : public ThreadResource { + protected: + swoole::RWLock lock_; + zend_array ht; + + static void item_dtor(zval *pDest) { + ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest); + delete item; + } + + public: + ZendArray() : ThreadResource(), lock_(0) { + zend_hash_init(&ht, 0, NULL, item_dtor, 1); + } + + ~ZendArray() override { + zend_hash_destroy(&ht); + } + + void clean() { + lock_.lock(); + zend_hash_clean(&ht); + lock_.unlock(); + } + + void append(zval *zvalue); + + void add(zend_string *skey, zval *zvalue) { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_add_ptr(&ht, item->key, item); + } + + void add(zend::String &skey, zval *zvalue) { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_add_ptr(&ht, item->key, item); + } + + void add(zend_long index, zval *zvalue) { + auto item = new ArrayItem(zvalue); + zend_hash_index_add_ptr(&ht, index, item); + } + + bool index_exists(zend_long index) { + return index < (zend_long) zend_hash_num_elements(&ht); + } + + bool strkey_exists(zend::String &skey) { + return zend_hash_find_ptr(&ht, skey.get()) != NULL; + } + + bool intkey_exists(zend_long index) { + return zend_hash_index_find_ptr(&ht, index) != NULL; + } + + void strkey_offsetGet(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void strkey_offsetExists(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + RETVAL_BOOL(strkey_exists(skey)); + lock_.unlock(); + } + + void strkey_offsetUnset(zval *zkey) { + zend::String skey(zkey); + lock_.lock(); + zend_hash_del(&ht, skey.get()); + lock_.unlock(); + } + + void strkey_offsetSet(zval *zkey, zval *zvalue) { + zend::String skey(zkey); + auto item = new ArrayItem(zvalue); + item->setKey(skey); + lock_.lock(); + zend_hash_update_ptr(&ht, item->key, item); + lock_.unlock(); + } + + void strkey_incr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_incr(zend_long index, zval *zvalue, zval *return_value); + void strkey_decr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_decr(zend_long index, zval *zvalue, zval *return_value); + bool index_incr(zval *zkey, zval *zvalue, zval *return_value); + bool index_decr(zval *zkey, zval *zvalue, zval *return_value); + + void strkey_add(zval *zkey, zval *zvalue, zval *return_value); + void intkey_add(zend_long index, zval *zvalue, zval *return_value); + void strkey_update(zval *zkey, zval *zvalue, zval *return_value); + void intkey_update(zend_long index, zval *zvalue, zval *return_value); + + void count(zval *return_value) { + lock_.lock_rd(); + RETVAL_LONG(zend_hash_num_elements(&ht)); + lock_.unlock(); + } + + void keys(zval *return_value); + void values(zval *return_value); + void to_array(zval *return_value); + void find(zval *search, zval *return_value); + void sort(bool renumber); + + void intkey_offsetGet(zend_long index, zval *return_value) { + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void intkey_offsetExists(zend_long index, zval *return_value) { + lock_.lock_rd(); + RETVAL_BOOL(intkey_exists(index)); + lock_.unlock(); + } + + void intkey_offsetUnset(zend_long index) { + lock_.lock(); + zend_hash_index_del(&ht, index); + lock_.unlock(); + } + + void intkey_offsetSet(zend_long index, zval *zvalue) { + auto item = new ArrayItem(zvalue); + lock_.lock(); + zend_hash_index_update_ptr(&ht, index, item); + lock_.unlock(); + } + + bool index_offsetGet(zend_long index, zval *return_value); + bool index_offsetSet(zend_long index, zval *zvalue); + void index_offsetUnset(zend_long index); + void index_offsetExists(zend_long index, zval *return_value); + + static void incr_update(ArrayItem *item, zval *zvalue, zval *return_value); + static ArrayItem *incr_create(zval *zvalue, zval *return_value); + static ZendArray *from(zend_array *ht); +}; + +#define INIT_ARRAY_INCR_PARAMS \ + zval *zkey; \ + zval zvalue_, *zvalue = NULL; \ + \ + ZEND_PARSE_PARAMETERS_START(1, 2) \ + Z_PARAM_ZVAL(zkey) \ + Z_PARAM_OPTIONAL \ + Z_PARAM_ZVAL(zvalue) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + if (!zvalue) { \ + zvalue = &zvalue_; \ + ZVAL_LONG(zvalue, 1); \ + } + +#endif diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php new file mode 100644 index 00000000000..67cb6f30a3c --- /dev/null +++ b/ext-src/stubs/php_swoole.stub.php @@ -0,0 +1,127 @@ + | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_server.h" +#include "swoole_process_pool.h" +#include "php_swoole_http.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_ex_arginfo.h" +END_EXTERN_C() + +#include +#include + +#include "nlohmann/json.hpp" + +using json = nlohmann::json; + +namespace swoole { + +#ifdef TCP_INFO +static json get_socket_info(int fd); +#endif + +static std::string handle_get_all_unix_sockets(Server *_server, const std::string &msg) { + auto _result = json::parse(msg); + if (!_result.is_object() || _result.find("type") == _result.end()) { + json return_value{ + {"data", "require parameter type"}, + {"code", 4003}, + }; + return return_value.dump(); + } + + std::string _type = _result["type"]; + Worker *workers; + uint32_t worker_num; + + if (_type == "event") { + workers = _server->gs->event_workers.workers; + worker_num = _server->worker_num; + } else { + workers = _server->gs->task_workers.workers; + worker_num = _server->task_worker_num; + } + + json sockets = json::array(); + + SW_LOOP_N(worker_num) { + auto master_socket = workers[i].pipe_object->get_socket(true); + json master_socket_info = json::object({ + {"fd", master_socket->fd}, + {"events", master_socket->events}, + {"total_recv_bytes", master_socket->total_recv_bytes}, + {"total_send_bytes", master_socket->total_send_bytes}, + {"out_buffer_size", master_socket->out_buffer ? master_socket->out_buffer->length() : 0}, + }); + sockets.push_back(master_socket_info); + + auto worker_socket = workers[i].pipe_object->get_socket(false); + json worker_socket_info = json::object({ + {"fd", worker_socket->fd}, + {"events", worker_socket->events}, + {"total_recv_bytes", worker_socket->total_recv_bytes}, + {"total_send_bytes", worker_socket->total_send_bytes}, + {"out_buffer_size", worker_socket->out_buffer ? worker_socket->out_buffer->length() : 0}, + }); + sockets.push_back(worker_socket_info); + } + + json return_value{ + {"data", sockets}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_all_sockets(Server *, const std::string &msg) { + if (sw_reactor() == nullptr) { + json return_value{ + {"data", "No event loop created"}, + {"code", 4004}, + }; + return return_value.dump(); + } + + json sockets = json::array(); + sw_reactor()->foreach_socket([&sockets](int fd, network::Socket *socket) { + network::Address addr{}; + if (socket->socket_type > SW_SOCK_UNIX_DGRAM || socket->socket_type < SW_SOCK_TCP) { +#ifdef SO_DOMAIN + struct stat fdstat; + if (fstat(fd, &fdstat) == -1) { + return; + } + mode_t type = fdstat.st_mode & S_IFMT; + if (type == S_IFSOCK) { + int domain; + if (socket->get_option(SOL_SOCKET, SO_DOMAIN, &domain) < 0) { + return; + } + int type; + if (socket->get_option(SOL_SOCKET, SO_TYPE, &type) < 0) { + return; + } + addr.type = network::Socket::convert_to_type(domain, type); + socket->get_name(&addr); + } +#else + return; +#endif + } else { + addr = socket->info; + } + json info = json::object({ + {"fd", socket->fd}, + {"address", addr.get_ip()}, + {"port", addr.get_port()}, + {"events", socket->events}, + {"socket_type", socket->socket_type}, + {"fd_type", socket->fd_type}, + {"total_recv_bytes", socket->total_recv_bytes}, + {"total_send_bytes", socket->total_send_bytes}, + {"out_buffer_size", socket->out_buffer ? socket->out_buffer->length() : 0}, + }); + sockets.push_back(info); + }); + + json return_value{ + {"data", sockets}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_all_commands(Server *serv, const std::string &msg) { + json command_list = json::array(); + for (auto &kv : serv->commands) { + json info = json::object({ + {"id", kv.second.id}, + {"name", kv.second.name}, + {"accepted_process_types", kv.second.accepted_process_types}, + }); + command_list.push_back(info); + }; + json return_value{ + {"data", command_list}, + {"code", 0}, + }; + return return_value.dump(); +} + +#ifdef TCP_INFO +static json get_socket_info(int fd) { + tcp_info info; + socklen_t len = sizeof(info); + if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &len) < 0) { + json return_value{ + {"data", "failed to getsockopt(TCP_INFO) for socket"}, + {"code", 5001}, + }; + return return_value.dump(); + } + + auto info_map = sw_socket_parse_tcp_info(&info); + json jinfo; + for (const auto &iter : info_map) { + jinfo[iter.first] = iter.second; + } + return jinfo; +} +#endif + +static json get_connection_info(Server *serv, Connection *conn) { + auto server_socket = serv->get_port_by_server_fd(conn->server_fd)->socket; + json info = json::object({ + {"session_id", conn->session_id}, + {"reactor_id", conn->reactor_id}, + {"fd", conn->fd}, + {"server_port", + std::string(server_socket->info.get_ip()) + ":" + std::to_string(server_socket->info.get_port())}, + {"address", conn->info.get_ip()}, + {"port", conn->info.get_port()}, + {"overflow", conn->overflow}, + {"connect_time", conn->connect_time}, + {"last_recv_time", conn->last_recv_time}, + {"last_send_time", conn->last_send_time}, + {"last_dispatch_time", conn->last_dispatch_time}, + {"recv_queued_bytes", conn->recv_queued_bytes}, + {"send_queued_bytes", conn->send_queued_bytes}, + {"total_recv_bytes", conn->socket->total_recv_bytes}, + {"total_send_bytes", conn->socket->total_send_bytes}, + {"uid", conn->uid}, + }); + return info; +} + +static std::string handle_get_socket_info(Server *serv, const std::string &msg) { + auto _result = json::parse(msg); + if (!_result.is_object() || _result.find("fd") == _result.end()) { + json return_value{ + {"data", "require parameter fd"}, + {"code", 4003}, + }; + return return_value.dump(); + } + +#ifndef TCP_INFO + json return_value{ + {"data", "platform unsupported"}, + {"code", 5001}, + }; +#else + std::string _fd = _result["fd"]; + int fd = std::atoi(_fd.c_str()); + json return_value{ + {"data", get_socket_info(fd)}, + {"code", 0}, + }; +#endif + return return_value.dump(); +} + +static std::string handle_get_thread_info(Server *serv, const std::string &msg) { + ReactorThread *thread = serv->get_thread(SwooleTG.id); + std::stringstream ss; + ss << std::this_thread::get_id(); + json jinfo{ + {"tid", ss.str()}, + {"id", thread->id}, + {"dispatch_count", thread->dispatch_count}, + {"event_num", SwooleTG.reactor->get_event_num()}, + {"timer_num", SwooleTG.timer ? SwooleTG.timer->count() : 0}, + }; + json return_value{ + {"data", jinfo}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_manager_info(Server *serv, const std::string &msg) { + ProcessPool *pool = (ProcessPool *) &serv->gs->event_workers; + json jinfo{ + {"pid", getpid()}, + {"reload_count", pool->reload_count}, + {"reload_last_time", pool->reload_last_time}, + }; + json return_value{ + {"data", jinfo}, + {"code", 0}, + }; + return return_value.dump(); +} + +static size_t get_socket_out_buffer_total_size() { + if (!sw_reactor()) { + return 0; + } + size_t size = 0; + for (auto &s : sw_reactor()->get_sockets()) { + if (s.second->out_buffer) { + size += s.second->out_buffer->length(); + } + } + return size; +} + +static std::string handle_get_memory_info(Server *serv, const std::string &msg) { + bool is_thread = serv->is_reactor_thread(); + + json jinfo{ + {"server", sizeof(Server)}, + {"workers", serv->get_all_worker_num() * sizeof(Worker)}, + {"connection_list", serv->get_max_connection() * sizeof(Connection)}, + {"session_list", SW_SESSION_LIST_SIZE * sizeof(Session)}, + {"global_memory", dynamic_cast(sw_mem_pool())->get_memory_size()}, + {"thread_global_memory", sw_tg_buffer()->size}, + {"message_bus", + is_thread ? serv->get_thread(SwooleTG.id)->message_bus.get_memory_size() + : serv->message_bus.get_memory_size()}, + {"socket_list", sw_reactor() ? sw_reactor()->get_sockets().size() * sizeof(network::Socket) : 0}, + {"socket_out_buffer", get_socket_out_buffer_total_size()}, + {"php_memory", is_thread ? 0 : zend_memory_usage(true)}, + }; + json return_value{ + {"data", jinfo}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_connections(Server *serv, const std::string &msg) { + json list = json::array(); + serv->foreach_connection([serv, &list](Connection *conn) { + if (serv->is_process_mode() && conn->reactor_id != SwooleTG.id) { + return; + } + if (serv->is_base_mode() && sw_worker() && conn->reactor_id != sw_worker()->id) { + return; + } + list.push_back(get_connection_info(serv, conn)); + }); + json return_value{ + {"data", list}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_connection_info(Server *serv, const std::string &msg) { + auto _result = json::parse(msg); + if (!_result.is_object() || _result.find("session_id") == _result.end()) { + json return_value{ + {"data", "require parameter session_id"}, + {"code", 4003}, + }; + return return_value.dump(); + } + + std::string _session_id = _result["session_id"]; + int session_id = std::atoi(_session_id.c_str()); + Connection *conn = serv->get_connection_verify(session_id); + if (!conn) { + json return_value{ + {"data", "connection not exists"}, + {"code", 4004}, + }; + return return_value.dump(); + } + + json return_value{ + {"data", get_connection_info(serv, conn)}, + {"code", 0}, + }; + return return_value.dump(); +} + +static std::string handle_get_all_ports(Server *serv, const std::string &msg) { + json _list = json::array(); + for (auto port : serv->ports) { + json info = json::object({ + {"host", port->host}, + {"port", port->port}, + {"backlog", port->backlog}, + {"type", port->type}, + {"ssl", port->ssl}, + {"protocols", port->get_protocols()}, + {"connection_num", (long) port->get_connection_num()}, + }); + _list.push_back(info); + }; + json return_value{ + {"data", _list}, + {"code", 0}, + }; + return return_value.dump(); +} + +void register_admin_server_commands(Server *serv) { + serv->add_command("get_all_sockets", Server::Command::ALL_PROCESS, handle_get_all_sockets); + serv->add_command("get_all_commands", Server::Command::ALL_PROCESS, handle_get_all_commands); + serv->add_command("get_socket_info", Server::Command::ALL_PROCESS, handle_get_socket_info); + serv->add_command("get_thread_info", Server::Command::ALL_PROCESS, handle_get_thread_info); + serv->add_command("get_manager_info", Server::Command::MANAGER, handle_get_manager_info); + serv->add_command("get_thread_info", Server::Command::ALL_PROCESS, handle_get_thread_info); + serv->add_command("get_memory_info", Server::Command::ALL_PROCESS, handle_get_memory_info); + serv->add_command("get_all_unix_sockets", Server::Command::ALL_PROCESS, handle_get_all_unix_sockets); + serv->add_command("get_all_ports", Server::Command::MASTER, handle_get_all_ports); + + int accepted_process_types; + if (serv->is_base_mode() || serv->single_thread) { + accepted_process_types = Server::Command::EVENT_WORKER | Server::Command::MASTER; + } else { + accepted_process_types = Server::Command::REACTOR_THREAD; + } + serv->add_command("get_connections", accepted_process_types, handle_get_connections); + serv->add_command("get_connection_info", accepted_process_types, handle_get_connection_info); +} +} // namespace swoole + +typedef std::function objects_store_iterator; + +static inline bool object_valid(zend_object *obj) { + return obj && IS_OBJ_VALID(obj) && obj->handlers && obj->handlers->get_class_name; +} + +static void objects_store_foreach(const objects_store_iterator &fn) { + for (uint32_t i = 0; i < EG(objects_store).top; i++) { + zend_object *obj = EG(objects_store).object_buckets[i]; + if (object_valid(obj)) { + fn(obj); + } + } +} + +static uint32_t object_store_count() { + uint32_t count = 0; + objects_store_foreach([&count](zend_object *obj) { count++; }); + return count; +} + +#ifdef TCP_INFO +// clang-format off +std::unordered_map sw_socket_parse_tcp_info(tcp_info *info) { +#if defined(__FreeBSD__) || defined(__NetBSD__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->__tcpi_ca_state}, + {"retransmits", info->__tcpi_retransmits}, + {"probes", info->__tcpi_probes}, + {"backoff", info->__tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->__tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->__tcpi_unacked}, + {"sacked", info->__tcpi_sacked}, + {"lost", info->__tcpi_lost}, + {"retrans", info->__tcpi_retrans}, + {"fackets", info->__tcpi_fackets}, + {"last_data_sent", info->__tcpi_last_data_sent}, + {"last_ack_sent", info->__tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->__tcpi_last_ack_recv}, + {"pmtu", info->__tcpi_pmtu}, + {"rcv_ssthresh", info->__tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->__tcpi_advmss}, + {"reordering", info->__tcpi_reordering}, + {"rcv_rtt", info->__tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_nxt", info->tcpi_snd_nxt}, + {"rcv_nxt", info->tcpi_rcv_nxt}, + {"toe_tid", info->tcpi_toe_tid}, + {"total_retrans", info->tcpi_snd_rexmitpack}, + {"rcv_ooopack", info->tcpi_rcv_ooopack}, + {"snd_zerowin", info->tcpi_snd_zerowin}, + }; +#elif defined(__OpenBSD__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->__tcpi_ca_state}, + {"retransmits", info->__tcpi_retransmits}, + {"probes", info->__tcpi_probes}, + {"backoff", info->__tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->__tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->__tcpi_unacked}, + {"sacked", info->__tcpi_sacked}, + {"lost", info->__tcpi_lost}, + {"retrans", info->__tcpi_retrans}, + {"fackets", info->__tcpi_fackets}, + {"last_data_sent", info->tcpi_last_data_sent}, + {"last_ack_sent", info->tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->tcpi_last_ack_recv}, + {"pmtu", info->__tcpi_pmtu}, + {"rcv_ssthresh", info->__tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->__tcpi_advmss}, + {"reordering", info->__tcpi_reordering}, + {"rcv_rtt", info->__tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_nxt", info->tcpi_snd_nxt}, + {"rcv_nxt", info->tcpi_rcv_nxt}, + {"toe_tid", info->tcpi_toe_tid}, + {"total_retrans", info->tcpi_snd_rexmitpack}, + {"rcv_ooopack", info->tcpi_rcv_ooopack}, + {"snd_zerowin", info->tcpi_snd_zerowin}, + }; +#elif defined(__linux__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->tcpi_ca_state}, + {"retransmits", info->tcpi_retransmits}, + {"probes", info->tcpi_probes}, + {"backoff", info->tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->tcpi_unacked}, + {"sacked", info->tcpi_sacked}, + {"lost", info->tcpi_lost}, + {"retrans", info->tcpi_retrans}, + {"fackets", info->tcpi_fackets}, + {"last_data_sent", info->tcpi_last_data_sent}, + {"last_ack_sent", info->tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->tcpi_last_ack_recv}, + {"pmtu", info->tcpi_pmtu}, + {"rcv_ssthresh", info->tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->tcpi_advmss}, + {"reordering", info->tcpi_reordering}, + {"rcv_rtt", info->tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"total_retrans", info->tcpi_total_retrans}, + }; +#elif defined(__APPLE__) + return { + {"state", (uint32_t) info->tcpi_state}, + {"snd_wscale", (uint32_t) info->tcpi_snd_wscale}, + {"rcv_wscale", (uint32_t) info->tcpi_rcv_wscale}, + {"options", (uint32_t) info->tcpi_options}, + {"flags", (uint32_t) info->tcpi_flags}, + {"rto", info->tcpi_rto}, + {"maxseg", info->tcpi_maxseg}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_sbbytes", info->tcpi_snd_sbbytes}, + {"rcv_wnd", info->tcpi_rcv_wnd}, + {"srtt", info->tcpi_srtt}, + {"rttvar", info->tcpi_rttvar}, + }; +#else + return {}; +#endif +} +// clang-format on +#endif + +ZEND_FUNCTION(swoole_get_vm_status) { + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("object_num"), object_store_count()); + add_assoc_long_ex(return_value, ZEND_STRL("resource_num"), zend_array_count(&EG(regular_list))); +} + +ZEND_FUNCTION(swoole_get_objects) { + zend_objects_store *objects = &EG(objects_store); + if (objects->top <= 1) { + RETURN_FALSE; + } + + array_init(return_value); + objects_store_foreach([return_value](zend_object *obj) { + zval zobject; + ZVAL_OBJ(&zobject, obj); + zval_addref_p(&zobject); + add_next_index_zval(return_value, &zobject); + }); +} + +ZEND_FUNCTION(swoole_get_object_by_handle) { + zend_long handle; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(handle) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_objects_store *objects = &EG(objects_store); + if (objects->top <= 1 || handle >= objects->top) { + RETURN_FALSE; + } + + zend_object *obj = objects->object_buckets[handle]; + if (!object_valid(obj)) { + RETURN_FALSE; + } + GC_ADDREF(obj); + RETURN_OBJ(obj); +} diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index a1350c2e470..896728b4cde 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -33,7 +33,7 @@ struct DNSCacheEntity { time_t update_time; }; -static std::unordered_map request_cache_map; +static SW_THREAD_LOCAL std::unordered_map request_cache_map; void php_swoole_async_coro_rshutdown() { for (auto i = request_cache_map.begin(); i != request_cache_map.end(); i++) { @@ -44,27 +44,42 @@ void php_swoole_async_coro_rshutdown() { void php_swoole_set_aio_option(HashTable *vht) { zval *ztmp; /* AIO */ - if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_core_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { - SwooleG.aio_max_wait_time = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { - SwooleG.aio_max_idle_time = zval_get_double(ztmp); - } + if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_core_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { + SwooleG.aio_max_wait_time = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { + SwooleG.aio_max_idle_time = zval_get_double(ztmp); + } +#ifdef SW_USE_IOURING + if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + if (php_swoole_array_get_value(vht, "iouring_workers", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_workers = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + if (php_swoole_array_get_value(vht, "iouring_flag", ztmp)) { + SwooleG.iouring_flag = zval_get_long(ztmp); + } +#endif } PHP_FUNCTION(swoole_async_set) { + SW_MUST_BE_MAIN_THREAD(); if (sw_reactor()) { php_swoole_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings"); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); RETURN_FALSE; } @@ -111,19 +126,24 @@ PHP_FUNCTION(swoole_async_set) { SwooleG.use_async_resolver = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - SWOOLE_G(enable_coroutine) = zval_is_true(ztmp); + SwooleG.enable_coroutine = zval_is_true(ztmp); } + RETURN_TRUE; } PHP_FUNCTION(swoole_async_dns_lookup_coro) { Coroutine::get_current_safe(); zval *domain; - long type = AF_INET; + zend_long type = AF_INET; double timeout = swoole::network::Socket::default_dns_timeout; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|dl", &domain, &timeout, &type) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(domain) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (Z_TYPE_P(domain) != IS_STRING) { php_swoole_fatal_error(E_WARNING, "invalid domain name"); @@ -169,5 +189,5 @@ PHP_FUNCTION(swoole_async_dns_lookup_coro) { } memcpy(cache->address, Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); cache->address[Z_STRLEN_P(return_value)] = '\0'; - cache->update_time = Timer::get_absolute_msec() + (int64_t)(SwooleG.dns_cache_refresh_time * 1000); + cache->update_time = Timer::get_absolute_msec() + (int64_t) (SwooleG.dns_cache_refresh_time * 1000); } diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index 54bcf1f43ca..90dc7f4fe81 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -10,74 +10,16 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_private.h" #include "swoole_memory.h" -#ifdef HAVE_FUTEX -#include -#include - -static sw_inline int swoole_futex_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } - - int ret; - struct timespec _timeout; - - if (timeout > 0) { - _timeout.tv_sec = (long) timeout; - _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, nullptr, 0); - } else { - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, nullptr, nullptr, 0); - } - if (ret == SW_OK && sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } else { - return SW_ERR; - } -} - -static sw_inline int swoole_futex_wakeup(sw_atomic_t *atomic, int n) { - if (sw_atomic_cmp_set(atomic, 0, 1)) { - return syscall(SYS_futex, atomic, FUTEX_WAKE, n, nullptr, nullptr, 0); - } else { - return SW_OK; - } -} - -#else -static sw_inline int swoole_atomic_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { - return SW_OK; - } - timeout = timeout <= 0 ? INT_MAX : timeout; - int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); - while (timeout > 0) { - if ((int32_t) *atomic > i) { - return SW_OK; - } else { - usleep(1000); - timeout -= 0.001; - } - } - sw_atomic_fetch_add(atomic, 1); - return SW_ERR; -} - -static sw_inline int swoole_atomic_wakeup(sw_atomic_t *atomic, int n) { - if (1 == (int32_t) *atomic) { - return SW_OK; - } - sw_atomic_fetch_add(atomic, n); - return SW_OK; -} -#endif +BEGIN_EXTERN_C() +#include "stubs/php_swoole_atomic_arginfo.h" +END_EXTERN_C() zend_class_entry *swoole_atomic_ce; static zend_object_handlers swoole_atomic_handlers; @@ -184,74 +126,41 @@ SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_construct, 0, 0, 0) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_add, 0, 0, 0) - ZEND_ARG_INFO(0, add_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_sub, 0, 0, 0) - ZEND_ARG_INFO(0, sub_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_get, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_set, 0, 0, 1) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_cmpset, 0, 0, 2) - ZEND_ARG_INFO(0, cmp_value) - ZEND_ARG_INFO(0, new_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wakeup, 0, 0, 0) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_atomic_methods[] = { - PHP_ME(swoole_atomic, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, wait, arginfo_swoole_atomic_wait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, wakeup, arginfo_swoole_atomic_wakeup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, __construct, arginfo_class_Swoole_Atomic___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, add, arginfo_class_Swoole_Atomic_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, sub, arginfo_class_Swoole_Atomic_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, get, arginfo_class_Swoole_Atomic_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, set, arginfo_class_Swoole_Atomic_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, wait, arginfo_class_Swoole_Atomic_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, wakeup, arginfo_class_Swoole_Atomic_wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, cmpset, arginfo_class_Swoole_Atomic_cmpset, ZEND_ACC_PUBLIC) PHP_FE_END }; static const zend_function_entry swoole_atomic_long_methods[] = { - PHP_ME(swoole_atomic_long, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, __construct, arginfo_class_Swoole_Atomic_Long___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, add, arginfo_class_Swoole_Atomic_Long_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, sub, arginfo_class_Swoole_Atomic_Long_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, get, arginfo_class_Swoole_Atomic_Long_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, set, arginfo_class_Swoole_Atomic_Long_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, cmpset, arginfo_class_Swoole_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_atomic_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", "swoole_atomic", nullptr, swoole_atomic_methods); + SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", nullptr, swoole_atomic_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic); SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_atomic, php_swoole_atomic_create_object, php_swoole_atomic_free_object, AtomicObject, std); - SW_INIT_CLASS_ENTRY( - swoole_atomic_long, "Swoole\\Atomic\\Long", "swoole_atomic_long", nullptr, swoole_atomic_long_methods); + SW_INIT_CLASS_ENTRY(swoole_atomic_long, "Swoole\\Atomic\\Long", nullptr, swoole_atomic_long_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long); SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny); @@ -335,11 +244,7 @@ PHP_METHOD(swoole_atomic, wait) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wait(atomic, timeout)); -#else - SW_CHECK_RETURN(swoole_atomic_wait(atomic, timeout)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); } PHP_METHOD(swoole_atomic, wakeup) { @@ -351,11 +256,7 @@ PHP_METHOD(swoole_atomic, wakeup) { Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wakeup(atomic, (int) n)); -#else - SW_CHECK_RETURN(swoole_atomic_wakeup(atomic, n)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); } PHP_METHOD(swoole_atomic_long, __construct) { diff --git a/ext-src/swoole_channel_coro.cc b/ext-src/swoole_channel_coro.cc index 28f7165aada..a265ce45acc 100644 --- a/ext-src/swoole_channel_coro.cc +++ b/ext-src/swoole_channel_coro.cc @@ -21,6 +21,10 @@ #include "swoole_coroutine_channel.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_channel_coro_arginfo.h" +END_EXTERN_C() + using swoole::coroutine::Channel; static zend_class_entry *swoole_channel_coro_ce; @@ -43,32 +47,16 @@ static PHP_METHOD(swoole_channel_coro, isFull); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_pop, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_channel_coro_methods[] = { - PHP_ME(swoole_channel_coro, __construct, arginfo_swoole_channel_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, push, arginfo_swoole_channel_coro_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, pop, arginfo_swoole_channel_coro_pop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, isEmpty, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, isFull, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, stats, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, length, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, __construct, arginfo_class_Swoole_Coroutine_Channel___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, push, arginfo_class_Swoole_Coroutine_Channel_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, pop, arginfo_class_Swoole_Coroutine_Channel_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, isEmpty, arginfo_class_Swoole_Coroutine_Channel_isEmpty, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, isFull, arginfo_class_Swoole_Coroutine_Channel_isFull, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, close, arginfo_class_Swoole_Coroutine_Channel_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, stats, arginfo_class_Swoole_Coroutine_Channel_stats, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, length, arginfo_class_Swoole_Coroutine_Channel_length, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -80,7 +68,7 @@ static sw_inline ChannelObject *php_swoole_channel_coro_fetch_object(zend_object static sw_inline Channel *php_swoole_get_channel(zval *zobject) { Channel *chan = php_swoole_channel_coro_fetch_object(Z_OBJ_P(zobject))->chan; if (UNEXPECTED(!chan)) { - php_swoole_fatal_error(E_ERROR, "you must call Channel constructor first"); + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return chan; } @@ -118,8 +106,7 @@ static zend_object *php_swoole_channel_coro_create_object(zend_class_entry *ce) } void php_swoole_channel_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_channel_coro, "Swoole\\Coroutine\\Channel", nullptr, "Co\\Channel", swoole_channel_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_channel_coro, "Swoole\\Coroutine\\Channel", "Co\\Channel", swoole_channel_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_channel_coro); SW_SET_CLASS_CLONEABLE(swoole_channel_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_channel_coro, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index b01301d3505..1db29c46030 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,83 +22,56 @@ #include #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_client_arginfo.h" +END_EXTERN_C() + +using swoole::HttpProxy; +using swoole::PacketLength; using swoole::Protocol; -using swoole::network::Client; -using swoole::network::Socket; using swoole::Socks5Proxy; -using swoole::HttpProxy; using swoole::String; - -struct ClientCallback { - zend_fcall_info_cache cache_onConnect; - zend_fcall_info_cache cache_onReceive; - zend_fcall_info_cache cache_onClose; - zend_fcall_info_cache cache_onError; - zend_fcall_info_cache cache_onBufferFull; - zend_fcall_info_cache cache_onBufferEmpty; -#ifdef SW_USE_OPENSSL - zend_fcall_info_cache cache_onSSLReady; -#endif - zval _object; -}; +using swoole::network::Client; +using swoole::network::Socket; static std::unordered_map *> long_connections; zend_class_entry *swoole_client_ce; -static zend_object_handlers swoole_client_handlers; +zend_object_handlers swoole_client_handlers; static zend_class_entry *swoole_client_exception_ce; static zend_object_handlers swoole_client_exception_handlers; -struct ClientObject { - Client *cli; - zval *zsocket; - ClientCallback *cb; - zend_object std; -}; - -static void php_swoole_client_free(zval *zobject, Client *cli); static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, int port); -static sw_inline ClientObject *php_swoole_client_fetch_object(zend_object *obj) { - return (ClientObject *) ((char *) obj - swoole_client_handlers.offset); -} - -static sw_inline Client *php_swoole_client_get_cli(zval *zobject) { - return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli; -} - -static sw_inline void php_swoole_client_set_cli(zval *zobject, Client *cli) { +static sw_inline void php_swoole_client_set_cli(zval *zobject, swoole::network::Client *cli) { php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli = cli; } #ifdef SWOOLE_SOCKETS_SUPPORT -static sw_inline zval *php_swoole_client_get_zsocket(zval *zobject) { +static sw_inline zval *client_get_zsocket(zval *zobject) { return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket; } -static sw_inline void php_swoole_client_set_zsocket(zval *zobject, zval *zsocket) { +static sw_inline void client_set_zsocket(zval *zobject, zval *zsocket) { php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket = zsocket; } #endif -static sw_inline ClientCallback *php_swoole_client_get_cb(zval *zobject) { - return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cb; -} - -static sw_inline void php_swoole_client_set_cb(zval *zobject, ClientCallback *cb) { - php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cb = cb; -} - -static void php_swoole_client_free_object(zend_object *object) { +static void client_free_object(zend_object *object) { + auto client_obj = php_swoole_client_fetch_object(object); + if (client_obj->async) { + php_swoole_client_async_free_object(client_obj); + } zend_object_std_dtor(object); } -static zend_object *php_swoole_client_create_object(zend_class_entry *ce) { +static zend_object *client_create_object(zend_class_entry *ce) { ClientObject *client = (ClientObject *) zend_object_alloc(sizeof(ClientObject), ce); zend_object_std_init(&client->std, ce); object_properties_init(&client->std, ce); client->std.handlers = &swoole_client_handlers; + client->async = nullptr; return &client->std; } @@ -135,7 +108,7 @@ static int client_select_add(zval *sock_array, fd_set *fds, int *max_fd); static int client_select_wait(zval *sock_array, fd_set *fds); #endif -static sw_inline Client *client_get_ptr(zval *zobject) { +Client *php_swoole_client_get_cli_safe(zval *zobject) { Client *cli = php_swoole_client_get_cli(zobject); if (cli && cli->socket) { if (cli->active) { @@ -154,104 +127,47 @@ static sw_inline Client *client_get_ptr(zval *zobject) { } } swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_client_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); php_swoole_error(E_WARNING, "client is not connected to server"); return nullptr; } // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, async) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_recv, 0, 0, 0) - ZEND_ARG_INFO(0, size) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, ip) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_close, 0, 0, 0) - ZEND_ARG_INFO(0, force) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_shutdown, 0, 0, 1) - ZEND_ARG_INFO(0, how) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_client_methods[] = { - PHP_ME(swoole_client, __construct, arginfo_swoole_client_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, __destruct, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, set, arginfo_swoole_client_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, connect, arginfo_swoole_client_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, recv, arginfo_swoole_client_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, send, arginfo_swoole_client_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, sendfile, arginfo_swoole_client_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, sendto, arginfo_swoole_client_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, shutdown, arginfo_swoole_client_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, __construct, arginfo_class_Swoole_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, __destruct, arginfo_class_Swoole_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, set, arginfo_class_Swoole_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, connect, arginfo_class_Swoole_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, recv, arginfo_class_Swoole_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, send, arginfo_class_Swoole_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, sendfile, arginfo_class_Swoole_Client_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, sendto, arginfo_class_Swoole_Client_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, shutdown, arginfo_class_Swoole_Client_shutdown, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_client, enableSSL, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getPeerCert, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, verifyPeerCert, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, enableSSL, arginfo_class_Swoole_Client_enableSSL, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getPeerCert, arginfo_class_Swoole_Client_getPeerCert, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, verifyPeerCert, arginfo_class_Swoole_Client_verifyPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_client, isConnected, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getsockname, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getpeername, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, close, arginfo_swoole_client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, isConnected, arginfo_class_Swoole_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getsockname, arginfo_class_Swoole_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getpeername, arginfo_class_Swoole_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, close, arginfo_class_Swoole_Client_close, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_client, getSocket, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getSocket, arginfo_class_Swoole_Client_getSocket, ZEND_ACC_PUBLIC) #endif PHP_FE_END }; // clang-format on void php_swoole_client_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_client, "Swoole\\Client", "swoole_client", nullptr, swoole_client_methods); + SW_INIT_CLASS_ENTRY(swoole_client, "Swoole\\Client", nullptr, swoole_client_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client); SW_SET_CLASS_CLONEABLE(swoole_client, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_client, php_swoole_client_create_object, php_swoole_client_free_object, ClientObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_client, client_create_object, client_free_object, ClientObject, std); - SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, - "Swoole\\Client\\Exception", - nullptr, - nullptr, - nullptr, - swoole_exception); + SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, swoole_exception); zend_declare_property_long(swoole_client_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); @@ -403,32 +319,15 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - Protocol::LengthFunc func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))); - if (func != nullptr) { - cli->protocol.get_package_length = func; - break; - } - } - - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return false; - } - efree(func_name); - cli->protocol.get_package_length = php_swoole_length_func; - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - } - sw_zend_fci_cache_persist(fci_cache); - cli->protocol.private_data = fci_cache; - break; + auto fci_cache = sw_callable_create(ztmp); + if (!fci_cache) { + return false; } - + cli->protocol.get_package_length = php_swoole_length_func; + if (cli->protocol.private_data_1) { + sw_callable_free(cli->protocol.private_data_1); + } + cli->protocol.private_data_1 = fci_cache; cli->protocol.package_length_size = 0; cli->protocol.package_length_type = '\0'; cli->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; @@ -437,7 +336,7 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); cli->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } else { cli->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; @@ -446,18 +345,18 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { * socket send/recv buffer size */ if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(1, SW_MIN(v, INT_MAX)); cli->socket->set_buffer_size(value); cli->socket->buffer_size = value; } if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); cli->buffer_high_watermark = value; } if (php_swoole_array_get_value(vht, "buffer_low_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); cli->buffer_low_watermark = value; } @@ -572,15 +471,14 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { return true; } -static void php_swoole_client_free(zval *zobject, Client *cli) { +void php_swoole_client_free(zval *zobject, Client *cli) { if (cli->timer) { swoole_timer_del(cli->timer); cli->timer = nullptr; } - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - cli->protocol.private_data = nullptr; + if (cli->protocol.private_data_1) { + sw_callable_free(cli->protocol.private_data_1); + cli->protocol.private_data_1 = nullptr; } // long tcp connection, delete from php_sw_long_connections if (cli->keep) { @@ -598,25 +496,25 @@ static void php_swoole_client_free(zval *zobject, Client *cli) { delete cli; #ifdef SWOOLE_SOCKETS_SUPPORT - zval *zsocket = php_swoole_client_get_zsocket(zobject); + zval *zsocket = client_get_zsocket(zobject); if (zsocket) { sw_zval_free(zsocket); - php_swoole_client_set_zsocket(zobject, nullptr); + client_set_zsocket(zobject, nullptr); } #endif // unset object php_swoole_client_set_cli(zobject, nullptr); } -ssize_t php_swoole_length_func(Protocol *protocol, Socket *_socket, const char *data, uint32_t length) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; +ssize_t php_swoole_length_func(const Protocol *protocol, Socket *_socket, PacketLength *pl) { + zend::Callable *cb = (zend::Callable *) protocol->private_data_1; zval zdata; zval retval; ssize_t ret = -1; // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); - if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { + ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); + if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, cb->ptr(), 1, &zdata, &retval) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { ret = zval_get_long(&retval); @@ -635,7 +533,7 @@ static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, in } long type = Z_LVAL_P(ztype); - int client_type = php_swoole_socktype(type); + int client_type = php_swoole_get_socket_type(type); if ((client_type == SW_SOCK_TCP || client_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { php_swoole_fatal_error(E_WARNING, "The port is invalid"); swoole_set_last_error(SW_ERROR_INVALID_PARAMS); @@ -674,7 +572,7 @@ static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, in } } else { _create_socket: - cli = new Client(php_swoole_socktype(type), false); + cli = new Client(php_swoole_get_socket_type(type), false); if (cli->socket == nullptr) { php_swoole_sys_error(E_WARNING, "Client_create() failed"); zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); @@ -709,15 +607,16 @@ static PHP_METHOD(swoole_client, __construct) { size_t len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|bs", &type, &async, &id, &len) == FAILURE) { - php_swoole_fatal_error(E_ERROR, "socket type param is required"); + zend_throw_error(NULL, "socket type param is required"); RETURN_FALSE; } if (async) { - php_swoole_fatal_error(E_ERROR, "please install the ext-async extension, using Swoole\\Async\\Client"); + zend_throw_error(NULL, "The $async parameter is not supported"); + RETURN_FALSE; } - int client_type = php_swoole_socktype(type); + int client_type = php_swoole_get_socket_type(type); if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { const char *space, *class_name = get_active_class_name(&space); zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", @@ -733,12 +632,6 @@ static PHP_METHOD(swoole_client, __construct) { if (id) { zend_update_property_stringl(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), id, len); } - // init - php_swoole_client_set_cli(ZEND_THIS, nullptr); - php_swoole_client_set_cb(ZEND_THIS, nullptr); -#ifdef SWOOLE_SOCKETS_SUPPORT - php_swoole_client_set_zsocket(ZEND_THIS, nullptr); -#endif RETURN_TRUE; } @@ -750,12 +643,6 @@ static PHP_METHOD(swoole_client, __destruct) { if (cli) { sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_ce, nullptr, "close", nullptr); } - // free memory - ClientCallback *cb = php_swoole_client_get_cb(ZEND_THIS); - if (cb) { - efree(cb); - php_swoole_client_set_cb(ZEND_THIS, nullptr); - } } static PHP_METHOD(swoole_client, set) { @@ -830,12 +717,12 @@ static PHP_METHOD(swoole_client, connect) { if (cli->async_connect) { RETURN_TRUE; } - php_swoole_error(E_WARNING, - "connect to server[%s:%d] failed. Error: %s[%d]", - host, - (int) port, - swoole_strerror(swoole_get_last_error()), - swoole_get_last_error()); + php_swoole_core_error(E_WARNING, + "connect to server[%s:%d] failed. Error: %s[%d]", + host, + (int) port, + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); php_swoole_client_free(ZEND_THIS, cli); RETURN_FALSE; } @@ -858,7 +745,7 @@ static PHP_METHOD(swoole_client, send) { RETURN_FALSE; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -902,8 +789,8 @@ static PHP_METHOD(swoole_client, sendto) { php_swoole_client_set_cli(ZEND_THIS, cli); } - char addr[SW_IP_MAX_LENGTH]; - char ip[SW_IP_MAX_LENGTH]; + char addr[INET6_ADDRSTRLEN]; + char ip[INET6_ADDRSTRLEN]; /** * udg doesn't need to use ip and port, so we don't need to deal with SW_SOCK_UNIX_DGRAM @@ -956,7 +843,7 @@ static PHP_METHOD(swoole_client, sendfile) { RETURN_FALSE; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -999,7 +886,7 @@ static PHP_METHOD(swoole_client, recv) { flags = MSG_WAITALL; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1029,7 +916,6 @@ static PHP_METHOD(swoole_client, recv) { ret = cli->recv(cli, buf, buf_len, 0); if (ret < 0) { - swoole_set_last_error(errno); php_swoole_sys_error(E_WARNING, "recv() failed"); zend_update_property_long( swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); @@ -1102,7 +988,11 @@ static PHP_METHOD(swoole_client, recv) { break; } buffer->length += retval; - buf_len = protocol->get_package_length(protocol, cli->socket, buffer->str, buffer->length); + PacketLength pl{ + buffer->str, + (uint32_t) buffer->length, + }; + buf_len = protocol->get_package_length(protocol, cli->socket, &pl); if (buf_len == 0) { continue; } else if (buf_len < 0) { @@ -1154,7 +1044,6 @@ static PHP_METHOD(swoole_client, recv) { } if (ret < 0) { - swoole_set_last_error(errno); php_swoole_sys_error(E_WARNING, "recv() failed"); zend_update_property_long( swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); @@ -1188,7 +1077,7 @@ static PHP_METHOD(swoole_client, isConnected) { } static PHP_METHOD(swoole_client, getsockname) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1225,11 +1114,11 @@ static PHP_METHOD(swoole_client, getsockname) { #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_client, getSocket) { - zval *zsocket = php_swoole_client_get_zsocket(ZEND_THIS); + zval *zsocket = client_get_zsocket(ZEND_THIS); if (zsocket) { RETURN_ZVAL(zsocket, 1, 0); } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1244,12 +1133,12 @@ static PHP_METHOD(swoole_client, getSocket) { SW_ZVAL_SOCKET(return_value, socket_object); zsocket = sw_zval_dup(return_value); Z_TRY_ADDREF_P(zsocket); - php_swoole_client_set_zsocket(ZEND_THIS, zsocket); + client_set_zsocket(ZEND_THIS, zsocket); } #endif static PHP_METHOD(swoole_client, getpeername) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1325,32 +1214,49 @@ static PHP_METHOD(swoole_client, close) { } #ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client, enableSSL) { - Client *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } +bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { if (cli->socket->socket_type != SW_SOCK_TCP && cli->socket->socket_type != SW_SOCK_TCP6) { php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); - RETURN_FALSE; + return false; } if (cli->socket->ssl) { php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); - RETURN_FALSE; + return false; } - cli->enable_ssl_encrypt(); - zval *zset = sw_zend_read_property_ex(swoole_client_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + cli->open_ssl = 1; + zval *zset = sw_zend_read_property_ex(swoole_client_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (ZVAL_IS_ARRAY(zset)) { php_swoole_client_check_ssl_setting(cli, zset); } - if (cli->ssl_handshake() < 0) { + return cli->enable_ssl_encrypt() == SW_OK; +} + +static PHP_METHOD(swoole_client, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback) { + zend_throw_exception( + swoole_exception_ce, "sync client does not support `onSslReady` callback", SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } - RETURN_TRUE; + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { + RETURN_FALSE; + } + RETURN_BOOL(cli->ssl_handshake() == SW_OK); } static PHP_METHOD(swoole_client, getPeerCert) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1365,7 +1271,7 @@ static PHP_METHOD(swoole_client, getPeerCert) { } static PHP_METHOD(swoole_client, verifyPeerCert) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1382,7 +1288,7 @@ static PHP_METHOD(swoole_client, verifyPeerCert) { #endif static PHP_METHOD(swoole_client, shutdown) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1400,9 +1306,13 @@ PHP_FUNCTION(swoole_client_select) { uint32_t index = 0; double timeout = SW_CLIENT_CONNECT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!a!|d", &r_array, &w_array, &e_array, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); int maxevents = SW_MAX(SW_MAX(php_swoole_array_length_safe(r_array), php_swoole_array_length_safe(w_array)), php_swoole_array_length_safe(e_array)); diff --git a/ext-src/swoole_client_async.cc b/ext-src/swoole_client_async.cc new file mode 100644 index 00000000000..4c147f7ffeb --- /dev/null +++ b/ext-src/swoole_client_async.cc @@ -0,0 +1,547 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_client.h" +#include "swoole_mqtt.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_client_async_arginfo.h" +END_EXTERN_C() + +#include "ext/standard/basic_functions.h" + +using swoole::network::Client; +using swoole::network::Socket; + +static PHP_METHOD(swoole_client_async, __construct); +static PHP_METHOD(swoole_client_async, __destruct); +static PHP_METHOD(swoole_client_async, connect); +static PHP_METHOD(swoole_client_async, sleep); +static PHP_METHOD(swoole_client_async, wakeup); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client_async, enableSSL); +#endif +static PHP_METHOD(swoole_client_async, isConnected); +static PHP_METHOD(swoole_client_async, close); +static PHP_METHOD(swoole_client_async, on); + +static void client_onConnect(Client *cli); +static void client_onReceive(Client *cli, const char *data, size_t length); +static void client_onClose(Client *cli); +static void client_onError(Client *cli); +static void client_onBufferFull(Client *cli); +static void client_onBufferEmpty(Client *cli); + +zend_class_entry *swoole_client_async_ce; +static zend_object_handlers swoole_client_async_handlers; + +void php_swoole_client_async_free_object(ClientObject *client_obj) { + if (client_obj->async->onConnect) { + sw_callable_free(client_obj->async->onConnect); + } + if (client_obj->async->onReceive) { + sw_callable_free(client_obj->async->onReceive); + } + if (client_obj->async->onClose) { + sw_callable_free(client_obj->async->onClose); + } + if (client_obj->async->onError) { + sw_callable_free(client_obj->async->onError); + } + if (client_obj->async->onBufferFull) { + sw_callable_free(client_obj->async->onBufferFull); + } + if (client_obj->async->onBufferEmpty) { + sw_callable_free(client_obj->async->onBufferEmpty); + } +#ifdef SW_USE_OPENSSL + if (client_obj->async->onSSLReady) { + sw_callable_free(client_obj->async->onSSLReady); + } +#endif + delete client_obj->async; +} + +static sw_inline void client_execute_callback(zval *zobject, enum php_swoole_client_callback_type type) { + auto client_obj = php_swoole_client_fetch_object(zobject); + const char *callback_name; + zend::Callable *cb; + + switch (type) { + case SW_CLIENT_CB_onConnect: + callback_name = "onConnect"; + cb = client_obj->async->onConnect; + break; + case SW_CLIENT_CB_onError: + callback_name = "onError"; + cb = client_obj->async->onError; + break; + case SW_CLIENT_CB_onClose: + callback_name = "onClose"; + cb = client_obj->async->onClose; + break; + case SW_CLIENT_CB_onBufferFull: + callback_name = "onBufferFull"; + cb = client_obj->async->onBufferFull; + break; + case SW_CLIENT_CB_onBufferEmpty: + callback_name = "onBufferEmpty"; + cb = client_obj->async->onBufferEmpty; + break; +#ifdef SW_USE_OPENSSL + case SW_CLIENT_CB_onSSLReady: + callback_name = "onSSLReady"; + cb = client_obj->async->onSSLReady; + break; +#endif + default: + abort(); + return; + } + + if (!cb) { + php_swoole_fatal_error(E_WARNING, "%s has no %s callback", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name); + return; + } + + if (UNEXPECTED(sw_zend_call_function_ex2(NULL, cb->ptr(), 1, zobject, NULL) != SUCCESS)) { + php_swoole_fatal_error(E_WARNING, "%s->%s handler error", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name); + } +} + +// clang-format off +static const zend_function_entry swoole_client_async_methods[] = { + PHP_ME(swoole_client_async, __construct, arginfo_class_Swoole_Async_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, __destruct, arginfo_class_Swoole_Async_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, connect, arginfo_class_Swoole_Async_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_client_async, pause, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_client_async, resume, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC) +#ifdef SW_USE_OPENSSL + PHP_ME(swoole_client_async, enableSSL, arginfo_class_Swoole_Async_Client_enableSSL, ZEND_ACC_PUBLIC) +#endif + PHP_ME(swoole_client_async, isConnected, arginfo_class_Swoole_Async_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, close, arginfo_class_Swoole_Async_Client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, on, arginfo_class_Swoole_Async_Client_on, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_client_async_minit(int module_number) { + SW_INIT_CLASS_ENTRY_EX( + swoole_client_async, "Swoole\\Async\\Client", nullptr, swoole_client_async_methods, swoole_client); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_async); + SW_SET_CLASS_CLONEABLE(swoole_client_async, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_async, sw_zend_class_unset_property_deny); + + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onConnect"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onError"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onReceive"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onClose"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onBufferFull"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onBufferEmpty"), ZEND_ACC_PRIVATE); +#ifdef SW_USE_OPENSSL + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onSSLReady"), ZEND_ACC_PRIVATE); +#endif +} + +static void client_onReceive(Client *cli, const char *data, size_t length) { + zval *zobject = (zval *) cli->object; + auto client_obj = php_swoole_client_fetch_object(zobject); + zend_fcall_info_cache *fci_cache = client_obj->async->onReceive->ptr(); + zval args[2]; + + args[0] = *zobject; + ZVAL_STRINGL(&args[1], data, length); + + if (UNEXPECTED(sw_zend_call_function_ex2(NULL, fci_cache, 2, args, NULL) != SUCCESS)) { + php_swoole_fatal_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + + zval_ptr_dtor(&args[1]); +} + +static void client_onConnect(Client *cli) { + zval *zobject = (zval *) cli->object; +#ifdef SW_USE_OPENSSL + if (cli->ssl_wait_handshake) { + cli->ssl_wait_handshake = 0; + client_execute_callback(zobject, SW_CLIENT_CB_onSSLReady); + return; + } +#endif + client_execute_callback(zobject, SW_CLIENT_CB_onConnect); +} + +static void client_onClose(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onClose); + zval_ptr_dtor(zobject); +} + +static void client_onError(Client *cli) { + zval *zobject = (zval *) cli->object; + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); + client_execute_callback(zobject, SW_CLIENT_CB_onError); + zval_ptr_dtor(zobject); +} + +static void client_onBufferFull(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onBufferFull); +} + +static void client_onBufferEmpty(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onBufferEmpty); +} + +static PHP_METHOD(swoole_client_async, __construct) { + zend_long type = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type) == FAILURE) { + zend_throw_error(NULL, "socket type param is required"); + RETURN_FALSE; + } + + int client_type = php_swoole_get_socket_type(type); + if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { + const char *space, *class_name = get_active_class_name(&space); + zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", + class_name, + space, + get_active_function_name(), + 1, + type); + RETURN_FALSE; + } + + php_swoole_check_reactor(); + + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, __destruct) { + SW_PREVENT_USER_DESTRUCT(); + + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (cli && cli->active) { + sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_async_ce, NULL, "close", NULL); + } +} + +static Client *php_swoole_client_async_new(zval *zobject, char *host, int host_len, int port) { + zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0); + if (ztype == nullptr || ZVAL_IS_NULL(ztype)) { + php_swoole_fatal_error(E_ERROR, "failed to get swoole_client->type"); + return nullptr; + } + + long type = Z_LVAL_P(ztype); + int client_type = php_swoole_get_socket_type(type); + if ((client_type == SW_SOCK_TCP || client_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + php_swoole_fatal_error(E_WARNING, "The port is invalid"); + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + return nullptr; + } + + Client *cli = new Client(php_swoole_get_socket_type(type), true); + if (cli->socket == nullptr) { + php_swoole_sys_error(E_WARNING, "Client_create() failed"); + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); + delete cli; + return nullptr; + } + + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), cli->socket->fd); + +#ifdef SW_USE_OPENSSL + if (type & SW_SOCK_SSL) { + cli->enable_ssl_encrypt(); + } +#endif + + return cli; +} + +static PHP_METHOD(swoole_client_async, connect) { + char *host; + size_t host_len; + zend_long port = 0; + double timeout = SW_CLIENT_CONNECT_TIMEOUT; + zend_long sock_flag = 0; + + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_STRING(host, host_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(sock_flag) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (host_len == 0) { + php_swoole_fatal_error(E_WARNING, "The host is empty"); + RETURN_FALSE; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + if (client_obj->cli) { + php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); + RETURN_FALSE; + } + + if (!client_obj->async) { + php_swoole_fatal_error(E_WARNING, "async client is not initialized"); + RETURN_FALSE; + } + + auto cli = php_swoole_client_async_new(ZEND_THIS, host, host_len, port); + if (cli == NULL) { + RETURN_FALSE; + } + + zval *zset = sw_zend_read_property(swoole_client_async_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + php_swoole_client_check_setting(cli, zset); + } + if (!client_obj->async->onReceive) { + php_swoole_fatal_error(E_ERROR, "no 'onReceive' callback function"); + RETURN_FALSE; + } + if (cli->get_socket()->is_stream()) { + if (!client_obj->async->onConnect) { + php_swoole_fatal_error(E_ERROR, "no 'onConnect' callback function"); + RETURN_FALSE; + } + if (!client_obj->async->onError) { + php_swoole_fatal_error(E_ERROR, "no 'onError' callback function"); + RETURN_FALSE; + } + if (!client_obj->async->onClose) { + php_swoole_fatal_error(E_ERROR, "no 'onClose' callback function"); + RETURN_FALSE; + } + cli->onConnect = client_onConnect; + cli->onClose = client_onClose; + cli->onError = client_onError; + cli->onReceive = client_onReceive; + if (client_obj->async->onBufferFull) { + cli->onBufferFull = client_onBufferFull; + } + if (client_obj->async->onBufferEmpty) { + cli->onBufferEmpty = client_onBufferEmpty; + } + } else { + if (client_obj->async->onConnect) { + cli->onConnect = client_onConnect; + } + if (client_obj->async->onClose) { + cli->onClose = client_onClose; + } + if (client_obj->async->onError) { + cli->onError = client_onError; + } + cli->onReceive = client_onReceive; + } + + client_obj->async->_zobject = *ZEND_THIS; + client_obj->cli = cli; + cli->object = &client_obj->async->_zobject; + Z_TRY_ADDREF_P(ZEND_THIS); + + // nonblock async + if (cli->connect(cli, host, port, timeout, sock_flag) < 0) { + if (errno == 0) { + auto error = swoole_get_last_error(); + if (error == SW_ERROR_DNSLOOKUP_RESOLVE_FAILED) { + php_swoole_error(E_WARNING, + "connect to server[%s:%d] failed. Error: %s[%d]", + host, + (int) port, + swoole_strerror(error), + error); + } + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), error); + } else { + php_swoole_sys_error(E_WARNING, "connect to server[%s:%d] failed", host, (int) port); + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), errno); + } + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (cli && cli->onError == NULL) { + php_swoole_client_free(ZEND_THIS, cli); + zval_ptr_dtor(ZEND_THIS); + } + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, isConnected) { + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!cli->socket) { + RETURN_FALSE; + } + RETURN_BOOL(cli->active); +} + +static PHP_METHOD(swoole_client_async, close) { + int ret = 1; + + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (!cli || !cli->socket) { + php_swoole_fatal_error(E_WARNING, "client is not connected to the server"); + RETURN_FALSE; + } + if (cli->closed) { + php_swoole_error(E_WARNING, "client socket is closed"); + RETURN_FALSE; + } + if (cli->async && cli->active == 0) { + zval *zobject = ZEND_THIS; + zval_ptr_dtor(zobject); + } + ret = cli->close(); + php_swoole_client_free(ZEND_THIS, cli); + SW_CHECK_RETURN(ret); +} + +static PHP_METHOD(swoole_client_async, on) { + char *cb_name; + size_t cb_name_len; + zval *zcallback; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &cb_name, &cb_name_len, &zcallback) == FAILURE) { + RETURN_FALSE; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + auto cb = sw_callable_create(zcallback); + if (!cb) { + return; + } + + if (!client_obj->async) { + client_obj->async = new AsyncClientObject(); + } + + if (strncasecmp("connect", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onConnect"), zcallback); + if (client_obj->async->onConnect) { + sw_callable_free(client_obj->async->onConnect); + } + client_obj->async->onConnect = cb; + } else if (strncasecmp("receive", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onReceive"), zcallback); + if (client_obj->async->onReceive) { + sw_callable_free(client_obj->async->onReceive); + } + client_obj->async->onReceive = cb; + } else if (strncasecmp("close", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onClose"), zcallback); + if (client_obj->async->onClose) { + sw_callable_free(client_obj->async->onClose); + } + client_obj->async->onClose = cb; + } else if (strncasecmp("error", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onError"), zcallback); + if (client_obj->async->onError) { + sw_callable_free(client_obj->async->onError); + } + client_obj->async->onError = cb; + } else if (strncasecmp("bufferFull", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onBufferFull"), zcallback); + if (client_obj->async->onBufferFull) { + sw_callable_free(client_obj->async->onBufferFull); + } + client_obj->async->onBufferFull = cb; + } else if (strncasecmp("bufferEmpty", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onBufferEmpty"), zcallback); + if (client_obj->async->onBufferEmpty) { + sw_callable_free(client_obj->async->onBufferEmpty); + } + client_obj->async->onBufferEmpty = cb; + } else { + php_swoole_fatal_error(E_WARNING, "Unknown event callback type name '%s'", cb_name); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, sleep) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->sleep()); +} + +static PHP_METHOD(swoole_client_async, wakeup) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->wakeup()); +} + +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client_async, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback == nullptr) { + zend_throw_exception(swoole_exception_ce, "require `onSslReady` callback", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { + RETURN_FALSE; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + if (swoole_event_set(cli->socket, SW_EVENT_WRITE) < 0) { + RETURN_FALSE; + } + + if (client_obj->async->onSSLReady) { + sw_callable_free(client_obj->async->onSSLReady); + } + + auto cb = sw_callable_create(zcallback); + if (!cb) { + RETURN_FALSE; + } + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onSSLReady"), zcallback); + client_obj->async->onSSLReady = cb; + cli->ssl_wait_handshake = 1; + cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; + + RETURN_TRUE; +} +#endif diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 49dd4d2eda7..4db4778e1ff 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -20,11 +20,13 @@ #include "swoole_protocol.h" #include "swoole_proxy.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_client_coro_arginfo.h" +END_EXTERN_C() + +using swoole::String; using swoole::coroutine::Socket; using swoole::network::Address; -using swoole::Socks5Proxy; -using swoole::HttpProxy; -using swoole::String; #ifdef SW_USE_OPENSSL using swoole::SSLContext; #endif @@ -33,7 +35,10 @@ static zend_class_entry *swoole_client_coro_ce; static zend_object_handlers swoole_client_coro_handlers; struct ClientCoroObject { - Socket *sock; + Socket *socket; + zval zsocket; + /* safety zval */ + zval zobject; zend_object std; }; @@ -60,125 +65,125 @@ static PHP_METHOD(swoole_client_coro, getpeername); static PHP_METHOD(swoole_client_coro, close); SW_EXTERN_C_END -static Socket *client_coro_new(zval *zobject, int port = 0); -void php_swoole_client_coro_socket_free(Socket *cli); - // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_peek, 0, 0, 0) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, address) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recvfrom, 0, 0, 2) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(1, address) - ZEND_ARG_INFO(1, port) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_client_coro_methods[] = { - PHP_ME(swoole_client_coro, __construct, arginfo_swoole_client_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, __destruct, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, set, arginfo_swoole_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, connect, arginfo_swoole_client_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, recv, arginfo_swoole_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, peek, arginfo_swoole_client_coro_peek, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, send, arginfo_swoole_client_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, sendfile, arginfo_swoole_client_coro_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, sendto, arginfo_swoole_client_coro_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, recvfrom, arginfo_swoole_client_coro_recvfrom, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, __construct, arginfo_class_Swoole_Coroutine_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, set, arginfo_class_Swoole_Coroutine_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, connect, arginfo_class_Swoole_Coroutine_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, recv, arginfo_class_Swoole_Coroutine_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, peek, arginfo_class_Swoole_Coroutine_Client_peek, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, send, arginfo_class_Swoole_Coroutine_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, sendfile, arginfo_class_Swoole_Coroutine_Client_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, sendto, arginfo_class_Swoole_Coroutine_Client_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, recvfrom, arginfo_class_Swoole_Coroutine_Client_recvfrom, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_client_coro, enableSSL, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getPeerCert, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, verifyPeerCert, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, enableSSL, arginfo_class_Swoole_Coroutine_Client_enableSSL, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getPeerCert, arginfo_class_Swoole_Coroutine_Client_getPeerCert, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, verifyPeerCert, arginfo_class_Swoole_Coroutine_Client_verifyPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_client_coro, isConnected, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getsockname, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getpeername, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, close, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, exportSocket, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, isConnected, arginfo_class_Swoole_Coroutine_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getsockname, arginfo_class_Swoole_Coroutine_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getpeername, arginfo_class_Swoole_Coroutine_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, close, arginfo_class_Swoole_Coroutine_Client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, exportSocket, arginfo_class_Swoole_Coroutine_Client_exportSocket, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on -static sw_inline ClientCoroObject *php_swoole_client_coro_fetch_object(zend_object *obj) { +static sw_inline ClientCoroObject *client_coro_fetch_object(zend_object *obj) { return (ClientCoroObject *) ((char *) obj - swoole_client_coro_handlers.offset); } -static sw_inline ClientCoroObject *php_swoole_get_client(zval *zobject) { - return php_swoole_client_coro_fetch_object(Z_OBJ_P(zobject)); +static sw_inline ClientCoroObject *client_coro_get_client(zval *zobject) { + return client_coro_fetch_object(Z_OBJ_P(zobject)); } -static sw_inline Socket *php_swoole_get_sock(zval *zobject) { - return php_swoole_get_client(zobject)->sock; +static sw_inline Socket *client_coro_get_socket(zval *zobject) { + return client_coro_get_client(zobject)->socket; } -static void php_swoole_client_coro_free_object(zend_object *object) { - ClientCoroObject *client = php_swoole_client_coro_fetch_object(object); - if (client->sock) { - php_swoole_client_coro_socket_free(client->sock); +static void client_coro_free_object(zend_object *object) { + ClientCoroObject *client = client_coro_fetch_object(object); + if (client->socket) { + client->socket->close(); } zend_object_std_dtor(&client->std); } -static zend_object *php_swoole_client_coro_create_object(zend_class_entry *ce) { - ClientCoroObject *sock_t = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); - zend_object_std_init(&sock_t->std, ce); - object_properties_init(&sock_t->std, ce); - sock_t->std.handlers = &swoole_client_coro_handlers; - return &sock_t->std; +#define CLIENT_CORO_GET_SOCKET_SAFE(__sock) \ + SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ + if (!__sock) { \ + php_swoole_socket_set_error_properties( \ + ZEND_THIS, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); \ + RETURN_FALSE; \ + } + +static zend_object *client_coro_create_object(zend_class_entry *ce) { + ClientCoroObject *object = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); + zend_object_std_init(&object->std, ce); + object_properties_init(&object->std, ce); + object->std.handlers = &swoole_client_coro_handlers; + ZVAL_OBJ(&object->zobject, &object->std); + ZVAL_NULL(&object->zsocket); + return &object->std; +} + +static void client_coro_socket_dtor(ClientCoroObject *client) { + if (client->socket->protocol.private_data_1) { + sw_callable_free(client->socket->protocol.private_data_1); + client->socket->protocol.private_data_1 = nullptr; + } + client->socket = nullptr; + zend_update_property_null(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("socket")); + zend_update_property_bool(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("connected"), 0); + zval_ptr_dtor(&client->zsocket); + ZVAL_NULL(&client->zsocket); +} + +static Socket *client_coro_create_socket(zval *zobject, zend_long type) { + enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); + auto object = php_swoole_create_socket(socket_type); + if (UNEXPECTED(!object)) { + php_swoole_socket_set_error_properties(zobject, errno); + return nullptr; + } + auto client = client_coro_get_client(zobject); + ZVAL_OBJ(&client->zsocket, object); + auto *socket = php_swoole_get_socket(&client->zsocket); + + socket->set_dtor([client](Socket *_socket) { client_coro_socket_dtor(client); }); + + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), socket->get_fd()); + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &client->zsocket); + + socket->set_buffer_allocator(sw_zend_string_allocator()); + socket->set_zero_copy(true); + +#ifdef SW_USE_OPENSSL + if ((type & SW_SOCK_SSL) && !socket->enable_ssl_encrypt()) { + php_swoole_socket_set_error_properties(zobject, EISCONN); + client_coro_socket_dtor(client); + return nullptr; + } +#endif + + return socket; } void php_swoole_client_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_client_coro, "Swoole\\Coroutine\\Client", nullptr, "Co\\Client", swoole_client_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_client_coro, "Swoole\\Coroutine\\Client", "Co\\Client", swoole_client_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_coro); SW_SET_CLASS_CLONEABLE(swoole_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_client_coro, php_swoole_client_coro_create_object, php_swoole_client_coro_free_object, ClientCoroObject, std); + swoole_client_coro, client_coro_create_object, client_coro_free_object, ClientCoroObject, std); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("type"), SW_SOCK_TCP, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); @@ -189,297 +194,41 @@ void php_swoole_client_coro_minit(int module_number) { zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); } -static sw_inline Socket *client_get_ptr(zval *zobject, bool silent = false) { - Socket *cli = php_swoole_get_client(zobject)->sock; - if (cli) { - return cli; - } else { - if (!silent) { - zend_update_property_long( - swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("errMsg"), - swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); - } +static sw_inline Socket *client_coro_get_socket_for_connect(zval *zobject, int port) { + auto client = client_coro_get_client(zobject); + if (client->socket) { + php_swoole_socket_set_error_properties(zobject, EISCONN, strerror(EISCONN)); return nullptr; } -} -static Socket *client_coro_new(zval *zobject, int port) { - zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0); - zend_long type = zval_get_long(ztype); - enum swSocketType sock_type = php_swoole_socktype(type); - - if ((sock_type == SW_SOCK_TCP || sock_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + zval *ztype = sw_zend_read_property(swoole_client_coro_ce, zobject, ZEND_STRL("type"), 1); + auto socket_type = php_swoole_get_socket_type(zval_get_long(ztype)); + if ((socket_type == SW_SOCK_TCP || socket_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { php_swoole_fatal_error(E_WARNING, "The port is invalid"); return nullptr; } - php_swoole_check_reactor(); - Socket *cli = new Socket(sock_type); - if (UNEXPECTED(cli->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); - zend_update_property_string( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete cli; + auto sock = client_coro_create_socket(zobject, zval_get_long(ztype)); + if (!sock) { return nullptr; } - - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), cli->get_fd()); - - cli->set_buffer_allocator(sw_zend_string_allocator()); - cli->set_zero_copy(true); - -#ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) { - cli->enable_ssl_encrypt(); - } -#endif - - php_swoole_get_client(zobject)->sock = cli; - - return cli; -} - -static bool client_coro_close(zval *zobject) { - Socket *cli = php_swoole_get_sock(zobject); - if (cli) { - zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!cli->get_bound_cid()) { - php_swoole_get_client(zobject)->sock = nullptr; - } - php_swoole_client_coro_socket_free(cli); - return true; - } - return false; -} - -void php_swoole_client_coro_socket_free(Socket *cli) { - if (!cli->has_bound()) { - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - cli->protocol.private_data = nullptr; - } - } - if (cli->close()) { - delete cli; - } -} - -bool php_swoole_client_set(Socket *cli, zval *zset) { - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - /** - * timeout - */ - if (php_swoole_array_get_value(vht, "timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp)); - } - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_CONNECT); - } - if (php_swoole_array_get_value(vht, "read_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_READ); - } - if (php_swoole_array_get_value(vht, "write_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_WRITE); - } - std::string _bind_address; - int _bind_port = 0; - if (php_swoole_array_get_value(vht, "bind_port", ztmp)) { - zend_long v = zval_get_long(ztmp); - _bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - if (php_swoole_array_get_value(vht, "bind_address", ztmp)) { - zend::String tmp = ztmp; - _bind_address = tmp.to_std_string(); - } - if (!_bind_address.empty() && !cli->bind(_bind_address, _bind_port)) { - ret = false; - } - /** - * socket send/recv buffer size - */ - if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long size = zval_get_long(ztmp); - if (size <= 0) { - php_swoole_fatal_error(E_WARNING, "socket buffer size must be greater than 0, got " ZEND_LONG_FMT, size); - ret = false; - } else { - cli->set_option(SOL_SOCKET, SO_RCVBUF, size) && cli->set_option(SOL_SOCKET, SO_SNDBUF, size); - } - } - /** - * client: tcp_nodelay - */ - if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { - if (cli->get_type() == SW_SOCK_TCP || cli->get_type() != SW_SOCK_TCP6) { - cli->get_socket()->set_tcp_nodelay(zval_is_true(ztmp)); - } - } - /** - * openssl and protocol options - */ - if (!php_swoole_socket_set_protocol(cli, zset)) { - ret = false; - } - /** - * socks5 proxy - */ - if (php_swoole_array_get_value(vht, "socks5_host", ztmp)) { - zend::String host(ztmp); - if (php_swoole_array_get_value(vht, "socks5_port", ztmp)) { - if (cli->socks5_proxy == nullptr) { - cli->socks5_proxy = new Socks5Proxy(); - } - cli->socks5_proxy->host = host.to_std_string(); - cli->socks5_proxy->port = zval_get_long(ztmp); - cli->socks5_proxy->dns_tunnel = 1; - if (php_swoole_array_get_value(vht, "socks5_username", ztmp)) { - zend::String username(ztmp); - if (username.len() > 0 && php_swoole_array_get_value(vht, "socks5_password", ztmp)) { - zend::String password(ztmp); - if (password.len() > 0) { - cli->socks5_proxy->method = 0x02; - cli->socks5_proxy->username = username.to_std_string(); - cli->socks5_proxy->password = password.to_std_string(); - } - } else { - php_swoole_fatal_error(E_WARNING, "socks5_password should not be null"); - ret = false; - } - } - } else { - php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); - ret = false; - } - } - /** - * http proxy - */ - else if (php_swoole_array_get_value(vht, "http_proxy_host", ztmp)) { - zend::String host(ztmp); - if (php_swoole_array_get_value(vht, "http_proxy_port", ztmp)) { - if (cli->http_proxy == nullptr) { - cli->http_proxy = new HttpProxy(); - } - cli->http_proxy->proxy_host = host.to_std_string(); - cli->http_proxy->proxy_port = zval_get_long(ztmp); - if (php_swoole_array_get_value(vht, "http_proxy_username", ztmp) || - php_swoole_array_get_value(vht, "http_proxy_user", ztmp)) { - zend::String username(ztmp); - if (username.len() > 0 && php_swoole_array_get_value(vht, "http_proxy_password", ztmp)) { - zend::String password(ztmp); - if (password.len() > 0) { - cli->http_proxy->username = username.to_std_string(); - cli->http_proxy->password = password.to_std_string(); - } - } else { - php_swoole_fatal_error(E_WARNING, "http_proxy_password should not be null"); - ret = false; - } - } - } else { - php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); - ret = false; - } - } - - return ret; -} - -#ifdef SW_USE_OPENSSL -bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->protocols = v; - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->cert_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); - ret = false; - } - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->key_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); - ret = false; - } - } - if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require key file"); - } else if (!sock->get_ssl_context()->key_file.empty() && sock->get_ssl_context()->cert_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require cert file"); - } - if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { - sock->get_ssl_context()->passphrase = zend::String(ztmp).to_std_string(); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { - sock->get_ssl_context()->tls_host_name = zend::String(ztmp).to_std_string(); - /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ - sock->get_ssl_context()->disable_tls_host_name = sock->get_ssl_context()->tls_host_name.empty(); - } -#endif - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { - sock->get_ssl_context()->verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { - sock->get_ssl_context()->allow_self_signed = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { - sock->get_ssl_context()->cafile = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { - sock->get_ssl_context()->capath = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { - sock->get_ssl_context()->ciphers = zend::String(ztmp).to_std_string(); - } - if (!sock->ssl_check_context()) { - ret = false; + client->socket = sock; + zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(sock, zset); } - return ret; + return sock; } -#endif static PHP_METHOD(swoole_client_coro, __construct) { - if (php_swoole_get_client(ZEND_THIS)->sock) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - zend_long type = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) Z_PARAM_LONG(type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - int client_type = php_swoole_socktype(type); - if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { + enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); + if (socket_type < SW_SOCK_TCP || socket_type > SW_SOCK_UNIX_DGRAM) { const char *space, *class_name = get_active_class_name(&space); zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", class_name, @@ -489,16 +238,14 @@ static PHP_METHOD(swoole_client_coro, __construct) { type); RETURN_FALSE; } - + php_swoole_check_reactor(); zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); - RETURN_TRUE; } static PHP_METHOD(swoole_client_coro, __destruct) {} static PHP_METHOD(swoole_client_coro, set) { - Socket *cli = client_get_ptr(ZEND_THIS, true); zval *zset, *zsetting; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -510,8 +257,9 @@ static PHP_METHOD(swoole_client_coro, set) { } else { zsetting = sw_zend_read_and_convert_property_array(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + Socket *cli = client_coro_get_socket(ZEND_THIS); if (cli) { - RETURN_BOOL(php_swoole_client_set(cli, zset)); + RETURN_BOOL(php_swoole_socket_set(cli, zset)); } RETURN_TRUE; } @@ -537,32 +285,17 @@ static PHP_METHOD(swoole_client_coro, connect) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (cli) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), EISCONN); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), swoole_strerror(EISCONN)); + Socket *socket = client_coro_get_socket_for_connect(ZEND_THIS, port); + if (!socket) { RETURN_FALSE; } - - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) { + socket->set_timeout(timeout, Socket::TIMEOUT_CONNECT); + if (!socket->connect(host, port, sock_flag)) { + php_swoole_socket_set_error_properties(ZEND_THIS, socket); + socket->close(); RETURN_FALSE; } - - zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); - if (zset && ZVAL_IS_ARRAY(zset)) { - php_swoole_client_set(cli, zset); - } - - cli->set_timeout(timeout, Socket::TIMEOUT_CONNECT); - if (!cli->connect(host, port, sock_flag)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - client_coro_close(ZEND_THIS); - RETURN_FALSE; - } - cli->set_timeout(timeout, Socket::TIMEOUT_RDWR); + socket->set_timeout(timeout, Socket::TIMEOUT_RDWR); zend_update_property_bool(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1); RETURN_TRUE; } @@ -583,55 +316,58 @@ static PHP_METHOD(swoole_client_coro, send) { RETURN_FALSE; } - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); Socket::TimeoutSetter ts(cli, timeout, Socket::TIMEOUT_WRITE); ssize_t ret = cli->send_all(data, data_len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - RETVAL_FALSE; - } else { - if ((size_t) ret < data_len && cli->errCode) { - zend_update_property_long( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - } - RETURN_LONG(ret); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + + if ((size_t) ret < data_len && cli->errCode) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); } + RETURN_LONG(ret); } static PHP_METHOD(swoole_client_coro, sendto) { char *host; size_t host_len; - long port; + zend_long port; char *data; size_t len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &host, &host_len, &port, &data, &len) == FAILURE) { + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_LONG(port) + Z_PARAM_STRING(data, len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (len == 0) { RETURN_FALSE; } - if (len == 0) { + Socket *socket = nullptr; + auto client = client_coro_get_client(ZEND_THIS); + if (client->socket == nullptr) { + socket = client_coro_get_socket_for_connect(ZEND_THIS, 0); + } else { + socket = client->socket; + } + if (!socket) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (!cli) { - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) { - RETURN_FALSE; - } + if ((socket->get_type() == SW_SOCK_TCP || socket->get_type() == SW_SOCK_TCP6) && + (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + php_swoole_fatal_error(E_WARNING, "The port is invalid"); + RETURN_FALSE; } - ssize_t ret = cli->sendto(std::string(host, host_len), port, data, len); + ssize_t ret = socket->sendto(std::string(host, host_len), port, data, len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); RETURN_FALSE; } RETURN_TRUE; @@ -649,27 +385,29 @@ static PHP_METHOD(swoole_client_coro, recvfrom) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (!cli) { - cli = client_coro_new(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + Socket *socket = nullptr; + auto client = client_coro_get_client(ZEND_THIS); + if (client->socket == nullptr) { + socket = client_coro_get_socket_for_connect(ZEND_THIS, 0); + } else { + socket = client->socket; + } + if (!socket) { + RETURN_FALSE; } zend_string *retval = zend_string_alloc(length, 0); - ssize_t n_bytes = cli->recvfrom(ZSTR_VAL(retval), length); + ssize_t n_bytes = socket->recvfrom(ZSTR_VAL(retval), length); if (n_bytes < 0) { zend_string_free(retval); - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); RETURN_FALSE; } else { zval_ptr_dtor(address); - ZVAL_STRING(address, cli->get_ip()); + ZVAL_STRING(address, socket->get_ip()); if (port) { zval_ptr_dtor(port); - ZVAL_LONG(port, cli->get_port()); + ZVAL_LONG(port, socket->get_port()); } ZSTR_LEN(retval) = n_bytes; @@ -692,10 +430,8 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); + // only stream socket can sendfile if (!(cli->get_type() == SW_SOCK_TCP || cli->get_type() == SW_SOCK_TCP6 || cli->get_type() == SW_SOCK_UNIX_STREAM)) { @@ -705,8 +441,7 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } if (!cli->sendfile(file, offset, length)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETVAL_FALSE; } else { RETVAL_TRUE; @@ -721,10 +456,7 @@ static PHP_METHOD(swoole_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); ssize_t retval; zend_string *result = nullptr; @@ -749,8 +481,7 @@ static PHP_METHOD(swoole_client_coro, recv) { } } if (retval < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } else if (retval == 0) { RETURN_EMPTY_STRING(); @@ -771,16 +502,12 @@ static PHP_METHOD(swoole_client_coro, peek) { Z_PARAM_LONG(buf_len) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); - buf = (char *) emalloc(buf_len + 1); + buf = (char *) emalloc((size_t)buf_len + 1); ret = cli->peek(buf, buf_len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); efree(buf); RETURN_FALSE; } else { @@ -791,7 +518,7 @@ static PHP_METHOD(swoole_client_coro, peek) { } static PHP_METHOD(swoole_client_coro, isConnected) { - Socket *cli = php_swoole_get_sock(ZEND_THIS); + Socket *cli = client_coro_get_socket(ZEND_THIS); if (cli && cli->is_connected()) { RETURN_TRUE; } else { @@ -800,15 +527,11 @@ static PHP_METHOD(swoole_client_coro, isConnected) { } static PHP_METHOD(swoole_client_coro, getsockname) { - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); Address sa; if (!cli->getsockname(&sa)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } @@ -816,8 +539,7 @@ static PHP_METHOD(swoole_client_coro, getsockname) { zval zaddress; ZVAL_STRING(&zaddress, sa.get_ip()); add_assoc_zval(return_value, "host", &zaddress); /* backward compatibility */ - Z_ADDREF(zaddress); - add_assoc_zval(return_value, "address", &zaddress); + zend::array_set(return_value, SW_STRL("address"), &zaddress); add_assoc_long(return_value, "port", sa.get_port()); } @@ -825,32 +547,16 @@ static PHP_METHOD(swoole_client_coro, getsockname) { * export Swoole\Coroutine\Socket object */ static PHP_METHOD(swoole_client_coro, exportSocket) { - zval rv; - zval *zsocket = zend_read_property(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("socket"), 1, &rv); - if (!ZVAL_IS_NULL(zsocket)) { - RETURN_ZVAL(zsocket, 1, 0); - } - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } - if (!php_swoole_export_socket(return_value, cli)) { - RETURN_FALSE; - } - zend_update_property(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("socket"), return_value); + auto cli = client_coro_get_client(ZEND_THIS); + RETURN_ZVAL(&cli->zsocket, 1, 0); } static PHP_METHOD(swoole_client_coro, getpeername) { - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); Address sa; if (!cli->getpeername(&sa)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } @@ -864,42 +570,45 @@ static PHP_METHOD(swoole_client_coro, getpeername) { } static PHP_METHOD(swoole_client_coro, close) { - RETURN_BOOL(client_coro_close(ZEND_THIS)); + CLIENT_CORO_GET_SOCKET_SAFE(_socket); + zend_update_property_bool(Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); + if (!_socket->close()) { + php_swoole_socket_set_error_properties(ZEND_THIS, _socket); + RETURN_FALSE; + } + RETURN_TRUE; } #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client_coro, enableSSL) { - Socket *cli = client_get_ptr(ZEND_THIS); - - if (!cli) { - RETURN_FALSE; - } - + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) { - php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); + php_swoole_socket_set_error_properties(ZEND_THIS, ESOCKTNOSUPPORT); RETURN_FALSE; } if (cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); + RETURN_FALSE; + } + if (!cli->enable_ssl_encrypt()) { + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); RETURN_FALSE; } - - cli->enable_ssl_encrypt(); - zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (php_swoole_array_length_safe(zset) > 0) { php_swoole_socket_set_ssl(cli, zset); } - RETURN_BOOL(cli->ssl_handshake()); + if (!cli->ssl_handshake()) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + RETURN_TRUE; } static PHP_METHOD(swoole_client_coro, getPeerCert) { - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (!cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); RETURN_FALSE; } if (!cli->get_socket()->ssl_get_peer_certificate(sw_tg_buffer())) { @@ -909,12 +618,9 @@ static PHP_METHOD(swoole_client_coro, getPeerCert) { } static PHP_METHOD(swoole_client_coro, verifyPeerCert) { - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (!cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + php_swoole_socket_set_error_properties(ZEND_THIS, ENOTCONN); RETURN_FALSE; } zend_bool allow_self_signed = 0; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index bd8eb9b7d4e..02ad505c131 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -18,6 +18,7 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" #include "php_swoole_coroutine_system.h" #include "swoole_server.h" @@ -26,9 +27,17 @@ #include "zend_builtin_functions.h" #include "ext/spl/spl_array.h" +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT +#include "zend_observer.h" +#endif + #include #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_arginfo.h" +END_EXTERN_C() + using std::unordered_map; using swoole::Coroutine; using swoole::PHPContext; @@ -36,53 +45,44 @@ using swoole::PHPCoroutine; using swoole::coroutine::Socket; using swoole::coroutine::System; -#define PHP_CORO_TASK_SLOT \ - ((int) ((ZEND_MM_ALIGNED_SIZE(sizeof(PHPContext)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval)) - 1) / \ - ZEND_MM_ALIGNED_SIZE(sizeof(zval)))) +#if PHP_VERSION_ID < 80100 +static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) { + zend_vm_stack page = (zend_vm_stack) emalloc(size); + + page->top = ZEND_VM_STACK_ELEMENTS(page); + page->end = (zval *) ((char *) page + size); + page->prev = prev; + return page; +} +#endif enum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 }; -bool PHPCoroutine::activated = false; -zend_array *PHPCoroutine::options = nullptr; +SW_THREAD_LOCAL bool PHPCoroutine::activated = false; +SW_THREAD_LOCAL zend_array *PHPCoroutine::options = nullptr; -PHPCoroutine::Config PHPCoroutine::config{ +SW_THREAD_LOCAL PHPCoroutine::Config PHPCoroutine::config{ SW_DEFAULT_MAX_CORO_NUM, 0, false, true, }; -PHPContext PHPCoroutine::main_task{}; -std::thread PHPCoroutine::interrupt_thread; -bool PHPCoroutine::interrupt_thread_running = false; +SW_THREAD_LOCAL PHPContext PHPCoroutine::main_context{}; +SW_THREAD_LOCAL std::thread PHPCoroutine::interrupt_thread; +SW_THREAD_LOCAL bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); -static zend_bool *zend_vm_interrupt = nullptr; +static zend_atomic_bool *zend_vm_interrupt = nullptr; +#if PHP_VERSION_ID < 80400 static user_opcode_handler_t ori_exit_handler = nullptr; +#endif static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; static unordered_map user_yield_coros; -#if PHP_VERSION_ID < 80000 -#define ZEND_ERROR_CB_LAST_ARG_D const char *format, va_list args -#define ZEND_ERROR_CB_LAST_ARG_RELAY format, args -#else -#define ZEND_ERROR_CB_LAST_ARG_D zend_string *message -#define ZEND_ERROR_CB_LAST_ARG_RELAY message -#endif - -#if PHP_VERSION_ID < 80100 -typedef const char error_filename_t; -#else -typedef zend_string error_filename_t; -#endif - static void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr; -static void (*orig_error_function)(int type, - error_filename_t *error_filename, - const uint32_t error_lineno, - ZEND_ERROR_CB_LAST_ARG_D) = nullptr; static zend_class_entry *swoole_coroutine_util_ce; static zend_class_entry *swoole_exit_exception_ce; @@ -108,118 +108,59 @@ static PHP_METHOD(swoole_coroutine, getStackUsage); static PHP_METHOD(swoole_coroutine, list); static PHP_METHOD(swoole_coroutine, enableScheduler); static PHP_METHOD(swoole_coroutine, disableScheduler); +#ifdef SW_CORO_TIME +static PHP_METHOD(swoole_coroutine, getExecuteTime); +#endif SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_set, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_cancel, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_resume, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_join, 0, 0, 1) - ZEND_ARG_INFO(0, cid_array) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_exists, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getContext, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_defer, 0, 0, 1) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_printBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getPcid, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getElapsed, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getStackUsage, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_coroutine_methods[] = { /** * Coroutine Core API */ - ZEND_FENTRY(create, ZEND_FN(swoole_coroutine_create), arginfo_swoole_coroutine_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(defer, ZEND_FN(swoole_coroutine_defer), arginfo_swoole_coroutine_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_scheduler, set, arginfo_swoole_coroutine_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, exists, arginfo_swoole_coroutine_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, cancel, arginfo_swoole_coroutine_cancel, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, join, arginfo_swoole_coroutine_join, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, isCanceled, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, suspend, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, resume, arginfo_swoole_coroutine_resume, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, stats, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getCid, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, getuid, getCid, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getPcid, arginfo_swoole_coroutine_getPcid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getContext, arginfo_swoole_coroutine_getContext, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getBackTrace, arginfo_swoole_coroutine_getBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, printBackTrace, arginfo_swoole_coroutine_printBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getElapsed, arginfo_swoole_coroutine_getElapsed, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getStackUsage, arginfo_swoole_coroutine_getStackUsage, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, list, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, enableScheduler, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, disableScheduler, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(create, ZEND_FN(swoole_coroutine_create), arginfo_class_Swoole_Coroutine_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(defer, ZEND_FN(swoole_coroutine_defer), arginfo_class_Swoole_Coroutine_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_scheduler, set, arginfo_class_Swoole_Coroutine_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_class_Swoole_Coroutine_getOptions, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, exists, arginfo_class_Swoole_Coroutine_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, yield, arginfo_class_Swoole_Coroutine_yield, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, cancel, arginfo_class_Swoole_Coroutine_cancel, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, join, arginfo_class_Swoole_Coroutine_join, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, isCanceled, arginfo_class_Swoole_Coroutine_isCanceled, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, suspend, yield, arginfo_class_Swoole_Coroutine_suspend, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, resume, arginfo_class_Swoole_Coroutine_resume, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, stats, arginfo_class_Swoole_Coroutine_stats, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getCid, arginfo_class_Swoole_Coroutine_getCid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, getuid, getCid, arginfo_class_Swoole_Coroutine_getuid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getPcid, arginfo_class_Swoole_Coroutine_getPcid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getContext, arginfo_class_Swoole_Coroutine_getContext, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getBackTrace, arginfo_class_Swoole_Coroutine_getBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, printBackTrace, arginfo_class_Swoole_Coroutine_printBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getElapsed, arginfo_class_Swoole_Coroutine_getElapsed, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getStackUsage, arginfo_class_Swoole_Coroutine_getStackUsage, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, list, arginfo_class_Swoole_Coroutine_list, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_class_Swoole_Coroutine_listCoroutines, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, enableScheduler, arginfo_class_Swoole_Coroutine_enableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, disableScheduler, arginfo_class_Swoole_Coroutine_disableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef SW_CORO_TIME + PHP_ME(swoole_coroutine, getExecuteTime, arginfo_class_Swoole_Coroutine_getExecuteTime, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#endif /** * Coroutine System API */ - ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_swoole_coroutine_system_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_swoole_coroutine_system_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, exec, arginfo_swoole_coroutine_system_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, sleep, arginfo_swoole_coroutine_system_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_swoole_coroutine_system_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, statvfs, arginfo_swoole_coroutine_system_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, readFile, arginfo_swoole_coroutine_system_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, writeFile, arginfo_swoole_coroutine_system_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, wait, arginfo_swoole_coroutine_system_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitPid, arginfo_swoole_coroutine_system_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitSignal, arginfo_swoole_coroutine_system_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitEvent, arginfo_swoole_coroutine_system_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_class_Swoole_Coroutine_System_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, exec, arginfo_class_Swoole_Coroutine_System_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, sleep, arginfo_class_Swoole_Coroutine_System_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_class_Swoole_Coroutine_System_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, statvfs, arginfo_class_Swoole_Coroutine_System_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, readFile, arginfo_class_Swoole_Coroutine_System_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, writeFile, arginfo_class_Swoole_Coroutine_System_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, wait, arginfo_class_Swoole_Coroutine_System_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -230,10 +171,15 @@ static const zend_function_entry swoole_coroutine_methods[] = static PHP_METHOD(swoole_exit_exception, getFlags); static PHP_METHOD(swoole_exit_exception, getStatus); +// clang-format off static const zend_function_entry swoole_exit_exception_methods[] = { - PHP_ME(swoole_exit_exception, getFlags, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_exit_exception, getStatus, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(swoole_exit_exception, getFlags, arginfo_class_Swoole_ExitException_getFlags, ZEND_ACC_PUBLIC) + PHP_ME(swoole_exit_exception, getStatus, arginfo_class_Swoole_ExitException_getStatus, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on +#if PHP_VERSION_ID < 80400 static int coro_exit_handler(zend_execute_data *execute_data) { zval ex; zend_object *obj; @@ -246,7 +192,7 @@ static int coro_exit_handler(zend_execute_data *execute_data) { } if (flags) { const zend_op *opline = EX(opline); - zval _exit_status; + zval _exit_status{}; zval *exit_status = nullptr; if (opline->op1_type != IS_UNUSED) { @@ -278,6 +224,48 @@ static int coro_exit_handler(zend_execute_data *execute_data) { return ZEND_USER_OPCODE_DISPATCH; } +#else +SW_EXTERN_C_BEGIN +PHP_FUNCTION(swoole_exit) { + zend_long flags = 0; + if (Coroutine::get_current()) { + flags |= SW_EXIT_IN_COROUTINE; + } + + if (sw_server() && sw_server()->is_started()) { + flags |= SW_EXIT_IN_SERVER; + } + + zend_string *message = NULL; + zend_long status = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_LONG(message, status) + ZEND_PARSE_PARAMETERS_END(); + + if (flags) { + zval ex = {}; + zend_object *obj = + zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : "swoole exit"), 0); + ZVAL_OBJ(&ex, obj); + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("flags"), flags); + if (message) { + zend_update_property_str(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), message); + } else { + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), status); + } + } else { + if (!php_swoole_call_original_handler(ZEND_STRL("exit"), INTERNAL_FUNCTION_PARAM_PASSTHRU)) { + if (message) { + php_write(ZSTR_VAL(message), ZSTR_LEN(message)); + } + sw_php_exit(status); + } + } +} +SW_EXTERN_C_END +#endif static int coro_begin_silence_handler(zend_execute_data *execute_data) { PHPContext *task = PHPCoroutine::get_context(); @@ -311,10 +299,92 @@ static void coro_interrupt_function(zend_execute_data *execute_data) { } } -void PHPCoroutine::init() { - Coroutine::set_on_yield(on_yield); - Coroutine::set_on_resume(on_resume); - Coroutine::set_on_close(on_close); +PHPContext *PHPCoroutine::create_context(Args *args) { + PHPContext *ctx = (PHPContext *) emalloc(sizeof(PHPContext)); + ctx->output_ptr = nullptr; +#if PHP_VERSION_ID < 80100 + ctx->array_walk_fci = nullptr; +#endif + ctx->in_silence = false; + + ctx->co = Coroutine::get_current(); + ctx->co->set_task((void *) ctx); + ctx->defer_tasks = nullptr; + ctx->pcid = ctx->co->get_origin_cid(); + ctx->context = nullptr; + ctx->on_yield = nullptr; + ctx->on_resume = nullptr; + ctx->on_close = nullptr; + ctx->enable_scheduler = true; + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_try_init(ctx); + ctx->fiber_init_notified = false; +#endif + + EG(vm_stack) = zend_vm_stack_new_page(SW_DEFAULT_PHP_STACK_PAGE_SIZE, nullptr); + EG(vm_stack_top) = EG(vm_stack)->top + ZEND_CALL_FRAME_SLOT; + EG(vm_stack_end) = EG(vm_stack)->end; + EG(vm_stack_page_size) = SW_DEFAULT_PHP_STACK_PAGE_SIZE; + + zend_function *func = EG(current_execute_data)->func; + zend_execute_data *call = (zend_execute_data *) (EG(vm_stack_top)); + EG(current_execute_data) = call; + memset(EG(current_execute_data), 0, sizeof(zend_execute_data)); + + EG(error_handling) = EH_NORMAL; + EG(exception_class) = nullptr; + EG(exception) = nullptr; + EG(jit_trace_num) = 0; + + call->func = func; + EG(vm_stack_top) += ZEND_CALL_FRAME_SLOT; + +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_base) = stack_base(ctx); + EG(stack_limit) = stack_limit(ctx); +#endif + + save_vm_stack(ctx); + record_last_msec(ctx); + + ctx->fci_cache = *args->fci_cache; + ctx->fci.size = sizeof(ctx->fci); + ctx->fci.object = NULL; + ctx->fci.param_count = args->argc; + ctx->fci.params = args->argv; + ctx->fci.named_params = NULL; + ctx->return_value = {}; + ctx->fci.retval = &ctx->return_value; + + if (args->callable) { + ctx->fci.function_name = *args->callable; + Z_TRY_ADDREF(ctx->fci.function_name); + } else { + ZVAL_UNDEF(&ctx->fci.function_name); + } + sw_zend_fci_cache_persist(&ctx->fci_cache); + + return ctx; +} + +void PHPCoroutine::bailout() { + Coroutine::bailout([]() { + if (sw_reactor()) { + sw_reactor()->running = false; + sw_reactor()->bailout = true; + } + zend_bailout(); + }); +} + +bool PHPCoroutine::catch_exception() { + if (UNEXPECTED(EG(exception))) { + // the exception error messages MUST be output on the current coroutine stack + zend_exception_error(EG(exception), E_ERROR); + return true; + } + return false; } void PHPCoroutine::activate() { @@ -322,12 +392,6 @@ void PHPCoroutine::activate() { return; } - if (zend_hash_str_find_ptr(&module_registry, ZEND_STRL("xdebug"))) { - php_swoole_fatal_error( - E_WARNING, - "Using Xdebug in coroutines is extremely dangerous, please notice that it may lead to coredump!"); - } - zval *enable_library = zend_get_constant_str(ZEND_STRL("SWOOLE_LIBRARY")); if (enable_library == NULL || !zval_is_true(enable_library)) { php_swoole_load_library(); @@ -340,34 +404,6 @@ void PHPCoroutine::activate() { orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = coro_interrupt_function; - /* replace the error function to save execute_data */ - orig_error_function = zend_error_cb; - zend_error_cb = - [](int type, error_filename_t *error_filename, const uint32_t error_lineno, ZEND_ERROR_CB_LAST_ARG_D) { - if (sw_unlikely(type & E_FATAL_ERRORS)) { - if (activated) { - /* update the last coroutine's info */ - save_task(get_context()); - } - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } -#ifdef SW_EXIT_WHEN_OCCURS_FATAL_ERROR - zend_try { - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - } - zend_catch { - exit(255); - } - zend_end_try(); -#endif - } - if (sw_likely(orig_error_function)) { - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - } - }; - if (SWOOLE_G(enable_preemptive_scheduler) || config.enable_preemptive_scheduler) { /* create a thread to interrupt the coroutine that takes up too much time */ interrupt_thread_start(); @@ -379,23 +415,33 @@ void PHPCoroutine::activate() { disable_unsafe_function(); - /** - * deactivate when reactor free. - */ + /* deactivate when reactor free */ sw_reactor()->add_destroy_callback(deactivate, nullptr); Coroutine::activate(); + + Coroutine::set_on_yield(on_yield); + Coroutine::set_on_resume(on_resume); + Coroutine::set_on_close(on_close); + activated = true; } void PHPCoroutine::deactivate(void *ptr) { + if (sw_unlikely(!activated)) { + return; + } + activated = false; interrupt_thread_stop(); /** * reset runtime hook */ disable_hook(); + Coroutine::set_on_yield(nullptr); + Coroutine::set_on_resume(nullptr); + Coroutine::set_on_close(nullptr); + zend_interrupt_function = orig_interrupt_function; - zend_error_cb = orig_error_function; if (config.enable_deadlock_check) { deadlock_check(); @@ -403,16 +449,17 @@ void PHPCoroutine::deactivate(void *ptr) { enable_unsafe_function(); Coroutine::deactivate(); - activated = false; } void PHPCoroutine::shutdown() { - interrupt_thread_stop(); - Coroutine::bailout(nullptr); + if (activated) { + deactivate(nullptr); + } if (options) { zend_array_destroy(options); options = nullptr; } + free_main_context(); } void PHPCoroutine::deadlock_check() { @@ -449,39 +496,12 @@ void PHPCoroutine::interrupt_thread_start() { interrupt_thread = std::thread([]() { swoole_signal_block_all(); while (interrupt_thread_running) { - *zend_vm_interrupt = 1; + zend_atomic_bool_store(zend_vm_interrupt, 1); std::this_thread::sleep_for(std::chrono::milliseconds(MAX_EXEC_MSEC / 2)); } }); } -inline void PHPCoroutine::vm_stack_init(void) { - uint32_t size = SW_DEFAULT_PHP_STACK_PAGE_SIZE; - zend_vm_stack page = (zend_vm_stack) emalloc(size); - - page->top = ZEND_VM_STACK_ELEMENTS(page); - page->end = (zval *) ((char *) page + size); - page->prev = nullptr; - - EG(vm_stack) = page; - EG(vm_stack)->top++; - EG(vm_stack_top) = EG(vm_stack)->top; - EG(vm_stack_end) = EG(vm_stack)->end; -#if PHP_VERSION_ID >= 70300 - EG(vm_stack_page_size) = size; -#endif -} - -inline void PHPCoroutine::vm_stack_destroy(void) { - zend_vm_stack stack = EG(vm_stack); - - while (stack != nullptr) { - zend_vm_stack p = stack->prev; - efree(stack); - stack = p; - } -} - /** * The meaning of the task argument in coro switch functions * @@ -491,81 +511,77 @@ inline void PHPCoroutine::vm_stack_destroy(void) { * close: current_task * */ -inline void PHPCoroutine::save_vm_stack(PHPContext *task) { -#ifdef SW_CORO_SWAP_BAILOUT - task->bailout = EG(bailout); -#endif - task->vm_stack_top = EG(vm_stack_top); - task->vm_stack_end = EG(vm_stack_end); - task->vm_stack = EG(vm_stack); -#if PHP_VERSION_ID >= 70300 - task->vm_stack_page_size = EG(vm_stack_page_size); -#endif - task->execute_data = EG(current_execute_data); -#if PHP_VERSION_ID >= 80000 - task->jit_trace_num = EG(jit_trace_num); -#endif - task->error_handling = EG(error_handling); - task->exception_class = EG(exception_class); - task->exception = EG(exception); +inline void PHPCoroutine::save_vm_stack(PHPContext *ctx) { + ctx->bailout = EG(bailout); + ctx->vm_stack_top = EG(vm_stack_top); + ctx->vm_stack_end = EG(vm_stack_end); + ctx->vm_stack = EG(vm_stack); + ctx->vm_stack_page_size = EG(vm_stack_page_size); + ctx->execute_data = EG(current_execute_data); + ctx->jit_trace_num = EG(jit_trace_num); + ctx->error_handling = EG(error_handling); + ctx->exception_class = EG(exception_class); + ctx->exception = EG(exception); #if PHP_VERSION_ID < 80100 if (UNEXPECTED(BG(array_walk_fci).size != 0)) { - if (!task->array_walk_fci) { - task->array_walk_fci = (zend::Function *) emalloc(sizeof(*task->array_walk_fci)); + if (!ctx->array_walk_fci) { + ctx->array_walk_fci = (zend::Function *) emalloc(sizeof(*ctx->array_walk_fci)); } - memcpy(task->array_walk_fci, &BG(array_walk_fci), sizeof(*task->array_walk_fci)); - memset(&BG(array_walk_fci), 0, sizeof(*task->array_walk_fci)); + memcpy(ctx->array_walk_fci, &BG(array_walk_fci), sizeof(*ctx->array_walk_fci)); + memset(&BG(array_walk_fci), 0, sizeof(*ctx->array_walk_fci)); } #endif - if (UNEXPECTED(task->in_silence)) { - task->tmp_error_reporting = EG(error_reporting); - EG(error_reporting) = task->ori_error_reporting; + if (UNEXPECTED(ctx->in_silence)) { + ctx->tmp_error_reporting = EG(error_reporting); + EG(error_reporting) = ctx->ori_error_reporting; } +#ifdef ZEND_CHECK_STACK_LIMIT + ctx->stack_base = EG(stack_base); + ctx->stack_limit = EG(stack_limit); +#endif } -inline void PHPCoroutine::restore_vm_stack(PHPContext *task) { -#ifdef SW_CORO_SWAP_BAILOUT - EG(bailout) = task->bailout; -#endif - EG(vm_stack_top) = task->vm_stack_top; - EG(vm_stack_end) = task->vm_stack_end; - EG(vm_stack) = task->vm_stack; -#if PHP_VERSION_ID >= 70300 - EG(vm_stack_page_size) = task->vm_stack_page_size; -#endif - EG(current_execute_data) = task->execute_data; -#if PHP_VERSION_ID >= 80000 - EG(jit_trace_num) = task->jit_trace_num; -#endif - EG(error_handling) = task->error_handling; - EG(exception_class) = task->exception_class; - EG(exception) = task->exception; +inline void PHPCoroutine::restore_vm_stack(PHPContext *ctx) { + EG(bailout) = ctx->bailout; + EG(vm_stack_top) = ctx->vm_stack_top; + EG(vm_stack_end) = ctx->vm_stack_end; + EG(vm_stack) = ctx->vm_stack; + EG(vm_stack_page_size) = ctx->vm_stack_page_size; + EG(current_execute_data) = ctx->execute_data; + EG(jit_trace_num) = ctx->jit_trace_num; + EG(error_handling) = ctx->error_handling; + EG(exception_class) = ctx->exception_class; + EG(exception) = ctx->exception; #if PHP_VERSION_ID < 80100 - if (UNEXPECTED(task->array_walk_fci && task->array_walk_fci->fci.size != 0)) { - memcpy(&BG(array_walk_fci), task->array_walk_fci, sizeof(*task->array_walk_fci)); - task->array_walk_fci->fci.size = 0; + if (UNEXPECTED(ctx->array_walk_fci && ctx->array_walk_fci->fci.size != 0)) { + memcpy(&BG(array_walk_fci), ctx->array_walk_fci, sizeof(*ctx->array_walk_fci)); + ctx->array_walk_fci->fci.size = 0; } #endif - if (UNEXPECTED(task->in_silence)) { - EG(error_reporting) = task->tmp_error_reporting; + if (UNEXPECTED(ctx->in_silence)) { + EG(error_reporting) = ctx->tmp_error_reporting; } +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_base) = ctx->stack_base; + EG(stack_limit) = ctx->stack_limit; +#endif } -inline void PHPCoroutine::save_og(PHPContext *task) { +inline void PHPCoroutine::save_og(PHPContext *ctx) { if (OG(handlers).elements) { - task->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); - memcpy(task->output_ptr, SWOG, sizeof(zend_output_globals)); + ctx->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); + memcpy(ctx->output_ptr, SWOG, sizeof(zend_output_globals)); php_output_activate(); } else { - task->output_ptr = nullptr; + ctx->output_ptr = nullptr; } } -inline void PHPCoroutine::restore_og(PHPContext *task) { - if (task->output_ptr) { - memcpy(SWOG, task->output_ptr, sizeof(zend_output_globals)); - efree(task->output_ptr); - task->output_ptr = nullptr; +inline void PHPCoroutine::restore_og(PHPContext *ctx) { + if (ctx->output_ptr) { + memcpy(SWOG, ctx->output_ptr, sizeof(zend_output_globals)); + efree(ctx->output_ptr); + ctx->output_ptr = nullptr; } } @@ -584,54 +600,69 @@ void PHPCoroutine::set_hook_flags(uint32_t flags) { config.hook_flags = flags; } -void PHPCoroutine::save_task(PHPContext *task) { - save_vm_stack(task); - save_og(task); +void PHPCoroutine::save_context(PHPContext *ctx) { + save_vm_stack(ctx); + save_og(ctx); } -void PHPCoroutine::restore_task(PHPContext *task) { - restore_vm_stack(task); - restore_og(task); +void PHPCoroutine::restore_context(PHPContext *ctx) { + restore_vm_stack(ctx); + restore_og(ctx); } void PHPCoroutine::on_yield(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *origin_task = get_origin_context(task); - save_task(task); - restore_task(origin_task); + PHPContext *ctx = (PHPContext *) arg; + PHPContext *origin_ctx = get_origin_context(ctx); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(ctx, origin_ctx); +#endif + save_context(ctx); + restore_context(origin_ctx); - if (task->on_yield) { - (*task->on_yield)(task); + if (ctx->on_yield) { + (*ctx->on_yield)(ctx); } - swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", task->co->get_cid(), task->co->get_origin_cid()); + swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", ctx->co->get_cid(), ctx->co->get_origin_cid()); } void PHPCoroutine::on_resume(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *current_task = get_context(); - save_task(current_task); - restore_task(task); - record_last_msec(task); + PHPContext *ctx = (PHPContext *) arg; + PHPContext *current_ctx = get_context(); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(current_ctx, ctx); +#endif + save_context(current_ctx); + restore_context(ctx); + record_last_msec(ctx); - if (task->on_resume) { - (*task->on_resume)(task); + if (ctx->on_resume) { + (*ctx->on_resume)(ctx); } - swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", Coroutine::get_current_cid(), task->co->get_cid()); + swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", Coroutine::get_current_cid(), ctx->co->get_cid()); } void PHPCoroutine::on_close(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *origin_task = get_origin_context(task); + PHPContext *ctx = (PHPContext *) arg; + if (ctx->on_close) { + (*ctx->on_close)(ctx); + } + efree(ctx); +} + +void PHPCoroutine::destroy_context(PHPContext *ctx) { + PHPContext *origin_ctx = get_origin_context(ctx); #ifdef SW_LOG_TRACE_OPEN // MUST be assigned here, the task memory may have been released - long cid = task->co->get_cid(); - long origin_cid = task->co->get_origin_cid(); + long cid = ctx->co->get_cid(); + long origin_cid = ctx->co->get_origin_cid(); #endif - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_CORO_STOP]) { - swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, task); + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_STOP)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, ctx); } if (OG(handlers).elements) { @@ -646,21 +677,39 @@ void PHPCoroutine::on_close(void *arg) { SG(request_info).no_headers = no_headers; } #if PHP_VERSION_ID < 80100 - if (task->array_walk_fci) { - efree(task->array_walk_fci); + if (ctx->array_walk_fci) { + efree(ctx->array_walk_fci); } #endif - if (task->on_close) { - (*task->on_close)(task); + if (ctx->defer_tasks) { + while (!ctx->defer_tasks->empty()) { + zend::Function *defer_fci = ctx->defer_tasks->top(); + ctx->defer_tasks->pop(); + sw_zend_fci_cache_discard(&defer_fci->fci_cache); + efree(defer_fci); + } + delete ctx->defer_tasks; + ctx->defer_tasks = nullptr; } - if (SwooleG.max_concurrency > 0 && task->pcid == -1) { - SwooleWG.worker_concurrency--; + // Release resources + if (ctx->context) { + zend_object *context = ctx->context; + ctx->context = (zend_object *) ~0; + OBJ_RELEASE(context); } - vm_stack_destroy(); - restore_task(origin_task); + Z_TRY_DELREF(ctx->fci.function_name); + ZVAL_UNDEF(&ctx->fci.function_name); + sw_zend_fci_cache_discard(&ctx->fci_cache); + + Z_TRY_DELREF(ctx->return_value); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(ctx, origin_ctx); + fiber_context_try_destroy(ctx); +#endif swoole_trace_log(SW_TRACE_COROUTINE, "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", @@ -669,201 +718,80 @@ void PHPCoroutine::on_close(void *arg) { (uintmax_t) Coroutine::count() - 1, (uintmax_t) zend_memory_usage(0), (uintmax_t) zend_memory_usage(1)); -} - -void PHPCoroutine::main_func(void *arg) { -#ifdef SW_CORO_SUPPORT_BAILOUT - zend_first_try { -#endif - Args *php_arg = (Args *) arg; - zend_fcall_info_cache fci_cache = *php_arg->fci_cache; - zend_function *func = fci_cache.function_handler; - zval *argv = php_arg->argv; - int argc = php_arg->argc; - PHPContext *task; - zend_execute_data *call; - zval _retval, *retval = &_retval; - - if (fci_cache.object) { - GC_ADDREF(fci_cache.object); - } - - vm_stack_init(); - call = (zend_execute_data *) (EG(vm_stack_top)); - task = (PHPContext *) EG(vm_stack_top); - EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); - -#if PHP_VERSION_ID < 70400 - call = zend_vm_stack_push_call_frame( - ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED, func, argc, fci_cache.called_scope, fci_cache.object); -#else - do { - uint32_t call_info; - void *object_or_called_scope; - if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache.object) { - object_or_called_scope = fci_cache.called_scope; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; - } else { - object_or_called_scope = fci_cache.object; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; - } - call = zend_vm_stack_push_call_frame(call_info, func, argc, object_or_called_scope); - } while (0); -#endif - - SW_LOOP_N(argc) { - zval *param; - zval *arg = &argv[i]; - if (Z_ISREF_P(arg) && !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - /* don't separate references for __call */ - arg = Z_REFVAL_P(arg); - } - param = ZEND_CALL_ARG(call, i + 1); - ZVAL_COPY(param, arg); - } - - call->symbol_table = nullptr; - - if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) { - uint32_t call_info; - GC_ADDREF(ZEND_CLOSURE_OBJECT(func)); - call_info = ZEND_CALL_CLOSURE; - ZEND_ADD_CALL_FLAG(call, call_info); - } - -#if defined(SW_CORO_SWAP_BAILOUT) && !defined(SW_CORO_SUPPORT_BAILOUT) - EG(bailout) = nullptr; -#endif - EG(current_execute_data) = call; - EG(error_handling) = EH_NORMAL; - EG(exception_class) = nullptr; - EG(exception) = nullptr; -#if PHP_VERSION_ID >= 80000 - EG(jit_trace_num) = 0; -#endif - - task->output_ptr = nullptr; -#if PHP_VERSION_ID < 80100 - task->array_walk_fci = nullptr; -#endif - task->in_silence = false; - task->co = Coroutine::get_current(); - task->co->set_task((void *) task); - task->defer_tasks = nullptr; - task->pcid = task->co->get_origin_cid(); - task->context = nullptr; - task->on_yield = nullptr; - task->on_resume = nullptr; - task->on_close = nullptr; - task->enable_scheduler = true; + zend_vm_stack_destroy(); + restore_context(origin_ctx); +} - save_vm_stack(task); - record_last_msec(task); +void PHPCoroutine::main_func(void *_args) { + bool exception_caught = false; + Args *args = (Args *) _args; + PHPContext *ctx = create_context(args); + zend_first_try { swoole_trace_log(SW_TRACE_COROUTINE, "Create coro id: %ld, origin cid: %ld, coro total count: %zu, heap size: %zu", - task->co->get_cid(), - task->co->get_origin_cid(), + ctx->co->get_cid(), + ctx->co->get_origin_cid(), (uintmax_t) Coroutine::count(), (uintmax_t) zend_memory_usage(0)); - if (SwooleG.max_concurrency > 0 && task->pcid == -1) { - // wait until concurrency slots are available - while (SwooleWG.worker_concurrency > SwooleG.max_concurrency - 1) { - swoole_trace_log(SW_TRACE_COROUTINE, - "php_coro cid=%ld waiting for concurrency slots: max: %d, used: %d", - task->co->get_cid(), - SwooleG.max_concurrency, - SwooleWG.worker_concurrency); - - swoole_event_defer( - [](void *data) { - Coroutine *co = (Coroutine *) data; - co->resume(); - }, - (void *) task->co); - task->co->yield(); - } - SwooleWG.worker_concurrency++; + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_START)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx); } - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_CORO_START]) { - swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, task); +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + if (EXPECTED(SWOOLE_G(enable_fiber_mock) && ctx->fci_cache.function_handler->type == ZEND_USER_FUNCTION)) { + zend_execute_data *tmp = EG(current_execute_data); + zend_execute_data call = {}; + EG(current_execute_data) = &call; + EG(current_execute_data)->opline = ctx->fci_cache.function_handler->op_array.opcodes; + call.func = ctx->fci_cache.function_handler; + fiber_context_switch_try_notify(get_origin_context(ctx), ctx); + EG(current_execute_data) = tmp; } +#endif + zend_call_function(&ctx->fci, &ctx->fci_cache); - if (EXPECTED(func->type == ZEND_USER_FUNCTION)) { - ZVAL_UNDEF(retval); - // TODO: enhancement it, separate execute data is necessary, but we lose the backtrace - EG(current_execute_data) = nullptr; - zend_init_func_execute_data(call, &func->op_array, retval); - zend_execute_ex(EG(current_execute_data)); - } else /* ZEND_INTERNAL_FUNCTION */ - { - ZVAL_NULL(retval); - call->prev_execute_data = nullptr; - call->return_value = nullptr; /* this is not a constructor call */ - execute_internal(call, retval); - zend_vm_stack_free_args(call); - } + // Catch exception in main function of the coroutine + exception_caught = catch_exception(); - if (task->defer_tasks) { - std::stack *tasks = task->defer_tasks; + // The defer tasks still need to be executed after an exception occurs + if (ctx->defer_tasks) { + std::stack *tasks = ctx->defer_tasks; while (!tasks->empty()) { zend::Function *defer_fci = tasks->top(); tasks->pop(); -#if PHP_VERSION_ID < 80000 - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; -#else - if (Z_TYPE_P(retval) != IS_UNDEF) { - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; - } -#endif - + if (Z_TYPE_P(&ctx->return_value) != IS_UNDEF) { + defer_fci->fci.param_count = 1; + defer_fci->fci.params = &ctx->return_value; + } if (UNEXPECTED(sw_zend_call_function_anyway(&defer_fci->fci, &defer_fci->fci_cache) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "defer callback handler error"); } + if (EG(exception)) { + zend_bailout(); + } sw_zend_fci_cache_discard(&defer_fci->fci_cache); efree(defer_fci); } - delete task->defer_tasks; - task->defer_tasks = nullptr; - } - - // resources release - if (task->context) { - zend_object *context = task->context; - task->context = (zend_object *) ~0; - OBJ_RELEASE(context); - } - if (fci_cache.object) { - OBJ_RELEASE(fci_cache.object); + delete ctx->defer_tasks; + ctx->defer_tasks = nullptr; } - zval_ptr_dtor(retval); - - // TODO: exceptions will only cause the coroutine to exit - if (UNEXPECTED(EG(exception))) { - zend_exception_error(EG(exception), E_ERROR); - } - -#ifdef SW_CORO_SUPPORT_BAILOUT } zend_catch { - Coroutine::bailout([]() { - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } - sw_zend_bailout(); - }); + // zend_bailout is executed in the c function + catch_exception(); + exception_caught = true; } zend_end_try(); -#endif + destroy_context(ctx); + if (exception_caught) { + bailout(); + } } -long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) { +long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable) { if (sw_unlikely(Coroutine::count() >= config.max_num)) { php_swoole_fatal_error(E_WARNING, "exceed max number of coroutine %zu", (uintmax_t) Coroutine::count()); return Coroutine::ERR_LIMIT; @@ -886,34 +814,200 @@ long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval _args.fci_cache = fci_cache; _args.argv = argv; _args.argc = argc; - save_task(get_context()); + _args.callable = callable; + save_context(get_context()); return Coroutine::create(main_func, (void *) &_args); } void PHPCoroutine::defer(zend::Function *fci) { - PHPContext *task = get_context(); - if (task->defer_tasks == nullptr) { - task->defer_tasks = new std::stack; + PHPContext *ctx = get_context(); + if (ctx->defer_tasks == nullptr) { + ctx->defer_tasks = new std::stack; } - task->defer_tasks->push(fci); + ctx->defer_tasks->push(fci); } -void php_swoole_coroutine_minit(int module_number) { - PHPCoroutine::init(); +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - SW_INIT_CLASS_ENTRY_BASE( - swoole_coroutine_util, "Swoole\\Coroutine", nullptr, "Co", swoole_coroutine_methods, nullptr); +void PHPCoroutine::fiber_context_init(PHPContext *ctx) { + zend_fiber_context *fiber_context = (zend_fiber_context *) emalloc(sizeof(*fiber_context)); + fiber_context->handle = (void *) -1; + fiber_context->kind = (void *) -1; + fiber_context->function = (zend_fiber_coroutine) -1; + fiber_context->stack = (zend_fiber_stack *) -1; + ctx->fiber_context = fiber_context; + + zend_observer_fiber_init_notify(fiber_context); +} + +void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_init(ctx); +} + +void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { + zend_observer_fiber_destroy_notify(ctx->fiber_context); + + if (ctx->fiber_context != NULL) { + efree(ctx->fiber_context); + } +} + +void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_destroy(ctx); +} + +zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *ctx) { + switch (ctx->co->get_state()) { + case Coroutine::STATE_INIT: + return ZEND_FIBER_STATUS_INIT; + case Coroutine::STATE_WAITING: + return ZEND_FIBER_STATUS_SUSPENDED; + case Coroutine::STATE_RUNNING: + return ZEND_FIBER_STATUS_RUNNING; + case Coroutine::STATE_END: + return ZEND_FIBER_STATUS_DEAD; + default: + php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status"); + return ZEND_FIBER_STATUS_DEAD; + } +} + +void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) { + zend_fiber_context *from_context = from->fiber_context; + zend_fiber_context *to_context = to->fiber_context; + + from_context->status = get_fiber_status(from); + to_context->status = get_fiber_status(to); + + if (!to->fiber_init_notified) { + to_context->status = ZEND_FIBER_STATUS_INIT; + zend_observer_fiber_switch_notify(from_context, to_context); + to_context->status = get_fiber_status(to); + to->fiber_init_notified = true; + } else { + zend_observer_fiber_switch_notify(from_context, to_context); + } +} + +void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_switch_notify(from, to); +} +#endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ + +#ifdef ZEND_CHECK_STACK_LIMIT +void *PHPCoroutine::stack_limit(PHPContext *ctx) { +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + zend_ulong reserve = EG(reserved_stack_size); + +#ifdef __APPLE__ + /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly + * probes a location that is twice the entered function's stack usage away + * from the stack pointer, when using an alternative stack. + * https://openradar.appspot.com/radar?id=5497722702397440 + */ + reserve = reserve * 2; +#endif + + if (!ctx->co) { + return nullptr; + } + + /* stack->pointer is the end of the stack */ + return (int8_t *) ctx->co->get_ctx().get_stack() + reserve; +#endif +} +void *PHPCoroutine::stack_base(PHPContext *ctx) { +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + if (!ctx->co) { + return nullptr; + } + + return (void *) ((uintptr_t) ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); +#endif +} +#endif /* ZEND_CHECK_STACK_LIMIT */ + +/* hook autoload */ + +static zend_class_entry *(*original_zend_autoload)(zend_string *name, zend_string *lc_name); + +struct AutoloadContext { + Coroutine *coroutine; + zend_class_entry *ce; +}; + +struct AutoloadQueue { + Coroutine *coroutine; + std::queue *queue; +}; + +static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) { + auto current = Coroutine::get_current(); + if (!current) { + return original_zend_autoload(name, lc_name); + } + + ZEND_ASSERT(EG(in_autoload) != nullptr); + zend_hash_del(EG(in_autoload), lc_name); + + if (UNEXPECTED(SWOOLE_G(in_autoload) == nullptr)) { + ALLOC_HASHTABLE(SWOOLE_G(in_autoload)); + zend_hash_init(SWOOLE_G(in_autoload), 8, nullptr, nullptr, 0); + } + zval *z_queue = zend_hash_find(SWOOLE_G(in_autoload), lc_name); + if (z_queue != nullptr) { + auto queue = (AutoloadQueue *) Z_PTR_P(z_queue); + if (queue->coroutine == current) { + return nullptr; + } + AutoloadContext context; + context.coroutine = current; + context.ce = nullptr; + queue->queue->push(&context); + current->yield(); + return context.ce; + } + AutoloadQueue queue; + queue.coroutine = current; + std::queue queue_object; + queue.queue = &queue_object; + + zend_hash_add_ptr(SWOOLE_G(in_autoload), lc_name, &queue); + zend_class_entry *ce = original_zend_autoload(name, lc_name); + zend_hash_del(SWOOLE_G(in_autoload), lc_name); + + AutoloadContext *pending_context = nullptr; + while (!queue_object.empty()) { + pending_context = queue_object.front(); + queue_object.pop(); + pending_context->ce = ce; + pending_context->coroutine->resume(); + } + return ce; +} + +void php_swoole_coroutine_minit(int module_number) { + SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_iterator, - "Swoole\\Coroutine\\Iterator", - nullptr, - "Co\\Iterator", - nullptr, - spl_ce_ArrayIterator); SW_INIT_CLASS_ENTRY_BASE( - swoole_coroutine_context, "Swoole\\Coroutine\\Context", nullptr, "Co\\Context", nullptr, spl_ce_ArrayObject); + swoole_coroutine_iterator, "Swoole\\Coroutine\\Iterator", "Co\\Iterator", nullptr, spl_ce_ArrayIterator); + SW_INIT_CLASS_ENTRY_BASE( + swoole_coroutine_context, "Swoole\\Coroutine\\Context", "Co\\Context", nullptr, spl_ce_ArrayObject); SW_REGISTER_LONG_CONSTANT("SWOOLE_DEFAULT_MAX_CORO_NUM", SW_DEFAULT_MAX_CORO_NUM); SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_MAX_NUM_LIMIT", Coroutine::MAX_NUM_LIMIT); @@ -923,23 +1017,26 @@ void php_swoole_coroutine_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_END", Coroutine::STATE_END); // prohibit exit in coroutine - SW_INIT_CLASS_ENTRY_EX(swoole_exit_exception, - "Swoole\\ExitException", - nullptr, - nullptr, - swoole_exit_exception_methods, - swoole_exception); + SW_INIT_CLASS_ENTRY_EX( + swoole_exit_exception, "Swoole\\ExitException", nullptr, swoole_exit_exception_methods, swoole_exception); zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL("flags"), 0, ZEND_ACC_PRIVATE); zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL("status"), 0, ZEND_ACC_PRIVATE); SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_COROUTINE", SW_EXIT_IN_COROUTINE); SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_SERVER", SW_EXIT_IN_SERVER); + + /* hook autoload */ + original_zend_autoload = zend_autoload; + zend_autoload = swoole_coroutine_autoload; + SWOOLE_G(in_autoload) = nullptr; } void php_swoole_coroutine_rinit() { if (SWOOLE_G(cli)) { +#if PHP_VERSION_ID < 80400 ori_exit_handler = zend_get_user_opcode_handler(ZEND_EXIT); zend_set_user_opcode_handler(ZEND_EXIT, coro_exit_handler); +#endif ori_begin_silence_handler = zend_get_user_opcode_handler(ZEND_BEGIN_SILENCE); zend_set_user_opcode_handler(ZEND_BEGIN_SILENCE, coro_begin_silence_handler); @@ -947,9 +1044,17 @@ void php_swoole_coroutine_rinit() { ori_end_silence_handler = zend_get_user_opcode_handler(ZEND_END_SILENCE); zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler); } + + PHPCoroutine::init_main_context(); } void php_swoole_coroutine_rshutdown() { + if (SWOOLE_G(in_autoload)) { + zend_hash_destroy(SWOOLE_G(in_autoload)); + FREE_HASHTABLE(SWOOLE_G(in_autoload)); + SWOOLE_G(in_autoload) = nullptr; + } + PHPCoroutine::shutdown(); } @@ -979,7 +1084,7 @@ PHP_FUNCTION(swoole_coroutine_create) { } } - long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params); + long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params, &fci.function_name); if (sw_likely(cid > 0)) { RETURN_LONG(cid); } else { @@ -1054,23 +1159,23 @@ static PHP_METHOD(swoole_coroutine, getContext) { Z_PARAM_LONG(cid) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PHPContext *task = + PHPContext *ctx = (PHPContext *) (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid)); - if (UNEXPECTED(!task)) { + if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); RETURN_NULL(); } - if (UNEXPECTED(task->context == (zend_object *) ~0)) { + if (UNEXPECTED(ctx->context == (zend_object *) ~0)) { /* bad context (has been destroyed), see: https://github.com/swoole/swoole-src/issues/2991 */ php_swoole_fatal_error(E_WARNING, "Context of this coroutine has been destroyed"); RETURN_NULL(); } - if (UNEXPECTED(!task->context)) { + if (UNEXPECTED(!ctx->context)) { object_init_ex(return_value, swoole_coroutine_context_ce); - task->context = Z_OBJ_P(return_value); + ctx->context = Z_OBJ_P(return_value); } - GC_ADDREF(task->context); - RETURN_OBJ(task->context); + GC_ADDREF(ctx->context); + RETURN_OBJ(ctx->context); } static PHP_METHOD(swoole_coroutine, getElapsed) { @@ -1087,28 +1192,17 @@ static PHP_METHOD(swoole_coroutine, getElapsed) { } static PHP_METHOD(swoole_coroutine, getStackUsage) { - zend_long current_cid = PHPCoroutine::get_cid(); - zend_long cid = current_cid; + zend_long cid = 0; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(cid) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); - if (UNEXPECTED(!task)) { - swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + ssize_t usage = PHPCoroutine::get_stack_usage(cid); + if (usage < 0) { RETURN_FALSE; } - - zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : task->vm_stack; - size_t usage = 0; - - while (stack) { - usage += (stack->end - stack->top) * sizeof(zval); - stack = stack->prev; - } - RETURN_LONG(usage); } @@ -1123,10 +1217,11 @@ static PHP_METHOD(swoole_coroutine, exists) { } static PHP_METHOD(swoole_coroutine, resume) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { - RETURN_FALSE; - } + zend_long cid; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); auto coroutine_iterator = user_yield_coros.find(cid); if (coroutine_iterator == user_yield_coros.end()) { @@ -1170,52 +1265,66 @@ static PHP_METHOD(swoole_coroutine, join) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - auto count = php_swoole_array_length(cid_array); - if (count == 0) { + if (php_swoole_array_length(cid_array) == 0) { swoole_set_last_error(SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } + std::set co_set; bool *canceled = new bool(false); - PHPContext::SwapCallback join_fn = [&count, canceled, co](PHPContext *task) { - if (--count > 0) { + + PHPContext::SwapCallback join_fn = [&co_set, canceled, co](PHPContext *task) { + co_set.erase(task); + if (!co_set.empty()) { return; } - swoole_event_defer([co, canceled](void*) { - if (*canceled == false) { - co->resume(); - } - delete canceled; - }, nullptr); + swoole_event_defer( + [co, canceled](void *) { + if (*canceled == false) { + co->resume(); + } + delete canceled; + }, + nullptr); }; zval *zcid; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cid_array), zcid) { long cid = zval_get_long(zcid); if (co->get_cid() == cid) { - swoole_set_last_error(SW_ERROR_WRONG_OPERATION); - php_swoole_error(E_WARNING, "can not join self"); + php_swoole_error_ex(E_WARNING, SW_ERROR_WRONG_OPERATION, "can not join self"); + delete canceled; RETURN_FALSE; } auto ctx = PHPCoroutine::get_context_by_cid(cid); if (ctx == nullptr) { - swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + continue; + } + if (ctx->on_close) { + swoole_set_last_error(SW_ERROR_CO_HAS_BEEN_BOUND); + delete canceled; RETURN_FALSE; } ctx->on_close = &join_fn; + co_set.insert(ctx); } ZEND_HASH_FOREACH_END(); + if (co_set.empty()) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + delete canceled; + RETURN_FALSE; + } + if (!co->yield_ex(timeout)) { - *canceled = true; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cid_array), zcid) { - long cid = zval_get_long(zcid); - auto ctx = PHPCoroutine::get_context_by_cid(cid); - if (ctx) { + if (!co_set.empty()) { + for (auto ctx : co_set) { ctx->on_close = nullptr; } + delete canceled; + } else { + *canceled = true; } - ZEND_HASH_FOREACH_END(); RETURN_FALSE; } @@ -1223,10 +1332,11 @@ static PHP_METHOD(swoole_coroutine, join) { } static PHP_METHOD(swoole_coroutine, cancel) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { - RETURN_FALSE; - } + zend_long cid; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Coroutine *co = swoole_coroutine_get(cid); if (!co) { @@ -1289,18 +1399,7 @@ static PHP_METHOD(swoole_coroutine, getBackTrace) { } } -static PHP_METHOD(swoole_coroutine, printBackTrace) { - zend_long cid = 0; - zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; - zend_long limit = 0; - - ZEND_PARSE_PARAMETERS_START(0, 3) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(cid) - Z_PARAM_LONG(options) - Z_PARAM_LONG(limit) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - +void sw_php_print_backtrace(zend_long cid, zend_long options, zend_long limit, zval *return_value) { zval argv[2]; ZVAL_LONG(&argv[0], options); ZVAL_LONG(&argv[1], limit); @@ -1308,18 +1407,36 @@ static PHP_METHOD(swoole_coroutine, printBackTrace) { if (!cid || cid == PHPCoroutine::get_cid()) { zend::function::call("debug_print_backtrace", 2, argv); } else { - PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); - if (UNEXPECTED(!task)) { + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); - RETURN_FALSE; + if (return_value) { + RETVAL_FALSE; + } + return; } zend_execute_data *ex_backup = EG(current_execute_data); - EG(current_execute_data) = task->execute_data; + EG(current_execute_data) = ctx->execute_data; zend::function::call("debug_print_backtrace", 2, argv); EG(current_execute_data) = ex_backup; } } +static PHP_METHOD(swoole_coroutine, printBackTrace) { + zend_long cid; + zend_long options = 0; + zend_long limit = 0; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + Z_PARAM_LONG(options) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + sw_php_print_backtrace(cid, options, limit, return_value); +} + static PHP_METHOD(swoole_coroutine, list) { zval zlist; array_init(&zlist); @@ -1336,6 +1453,12 @@ static PHP_METHOD(swoole_coroutine, list) { zval_ptr_dtor(&zlist); } +#ifdef SW_CORO_TIME +static PHP_METHOD(swoole_coroutine, getExecuteTime) { + RETURN_LONG(PHPCoroutine::get_execute_time()); +} +#endif + PHP_METHOD(swoole_coroutine, enableScheduler) { RETURN_BOOL(PHPCoroutine::enable_scheduler()); } diff --git a/ext-src/swoole_coroutine_lock.cc b/ext-src/swoole_coroutine_lock.cc new file mode 100644 index 00000000000..791ac906694 --- /dev/null +++ b/ext-src/swoole_coroutine_lock.cc @@ -0,0 +1,136 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_lock_arginfo.h" +END_EXTERN_C() + +using swoole::CoroutineLock; + +static zend_class_entry *swoole_coroutine_lock_ce; +static zend_object_handlers swoole_coroutine_lock_handlers; + +struct CoLockObject { + CoroutineLock *lock; + bool shared; + zend_object std; +}; + +static sw_inline CoLockObject *co_lock_fetch_object(zend_object *obj) { + return (CoLockObject *) ((char *) obj - swoole_coroutine_lock_handlers.offset); +} + +static CoroutineLock *co_lock_get_ptr(zval *zobject) { + return co_lock_fetch_object(Z_OBJ_P(zobject))->lock; +} + +static CoroutineLock *co_lock_get_and_check_ptr(zval *zobject) { + CoroutineLock *lock = co_lock_get_ptr(zobject); + if (UNEXPECTED(!lock)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return lock; +} + +void co_lock_set_ptr(zval *zobject, CoroutineLock *ptr) { + co_lock_fetch_object(Z_OBJ_P(zobject))->lock = ptr; +} + +static void co_lock_free_object(zend_object *object) { + CoLockObject *o = co_lock_fetch_object(object); + if (o->lock && !o->shared) { + delete o->lock; + } + zend_object_std_dtor(object); +} + +static zend_object *co_lock_create_object(zend_class_entry *ce) { + CoLockObject *lock = (CoLockObject *) zend_object_alloc(sizeof(CoLockObject), ce); + zend_object_std_init(&lock->std, ce); + object_properties_init(&lock->std, ce); + lock->std.handlers = &swoole_coroutine_lock_handlers; + return &lock->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_coroutine_lock, __construct); +static PHP_METHOD(swoole_coroutine_lock, __destruct); +static PHP_METHOD(swoole_coroutine_lock, lock); +static PHP_METHOD(swoole_coroutine_lock, trylock); +static PHP_METHOD(swoole_coroutine_lock, unlock); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_coroutine_lock_methods[] = +{ + PHP_ME(swoole_coroutine_lock, __construct, arginfo_class_Swoole_Coroutine_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, __destruct, arginfo_class_Swoole_Coroutine_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, lock, arginfo_class_Swoole_Coroutine_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, trylock, arginfo_class_Swoole_Coroutine_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, unlock, arginfo_class_Swoole_Coroutine_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_coroutine_lock_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_coroutine_lock, "Swoole\\Coroutine\\Lock", nullptr, swoole_coroutine_lock_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_lock); + SW_SET_CLASS_CLONEABLE(swoole_coroutine_lock, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_coroutine_lock, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_coroutine_lock, co_lock_create_object, co_lock_free_object, CoLockObject, std); + zend_declare_property_long(swoole_coroutine_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_coroutine_lock, __construct) { + CoroutineLock *lock = co_lock_get_ptr(ZEND_THIS); + if (lock != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_bool shared = false; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(shared) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + lock = new CoroutineLock(shared); + co_lock_set_ptr(ZEND_THIS, lock); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_coroutine_lock, __destruct) {} + +static PHP_METHOD(swoole_coroutine_lock, lock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_coroutine_lock, unlock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_coroutine_lock, trylock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 6ab6a345833..ad20b1e65e0 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -20,9 +20,14 @@ #include -using swoole::Reactor; +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_scheduler_arginfo.h" +END_EXTERN_C() + using swoole::Coroutine; +using swoole::NameResolver; using swoole::PHPCoroutine; +using swoole::Reactor; using swoole::coroutine::Socket; using swoole::coroutine::System; @@ -76,30 +81,12 @@ static void scheduler_free_object(zend_object *object) { } // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_add, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_parallel, 0, 0, 1) - ZEND_ARG_INFO(0, n) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_coroutine_scheduler_methods[] = { - PHP_ME(swoole_coroutine_scheduler, add, arginfo_swoole_coroutine_scheduler_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, parallel, arginfo_swoole_coroutine_scheduler_parallel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, set, arginfo_swoole_coroutine_scheduler_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, add, arginfo_class_Swoole_Coroutine_Scheduler_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, parallel, arginfo_class_Swoole_Coroutine_Scheduler_parallel, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, set, arginfo_class_Swoole_Coroutine_Scheduler_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_class_Swoole_Coroutine_Scheduler_getOptions, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, start, arginfo_class_Swoole_Coroutine_Scheduler_start, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -108,7 +95,6 @@ static const zend_function_entry swoole_coroutine_scheduler_methods[] = { void php_swoole_coroutine_scheduler_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_coroutine_scheduler, "Swoole\\Coroutine\\Scheduler", - nullptr, "Co\\Scheduler", swoole_coroutine_scheduler_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_scheduler); @@ -120,16 +106,15 @@ void php_swoole_coroutine_scheduler_minit(int module_number) { swoole_coroutine_scheduler_ce->ce_flags |= ZEND_ACC_FINAL; } -static zend_fcall_info_cache exit_condition_fci_cache; -static bool exit_condition_cleaner; +static zend::Callable *exit_condition_fci_cache = nullptr; static bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &event_num) { zval retval; int success; - SW_ASSERT(exit_condition_fci_cache.function_handler); + SW_ASSERT(exit_condition_fci_cache); ZVAL_NULL(&retval); - success = sw_zend_call_function_ex(nullptr, &exit_condition_fci_cache, 0, nullptr, &retval); + success = sw_zend_call_function_ex(nullptr, exit_condition_fci_cache->ptr(), 0, nullptr, &retval); if (UNEXPECTED(success != SUCCESS)) { php_swoole_fatal_error(E_ERROR, "Coroutine can_exit callback handler error"); } @@ -139,10 +124,27 @@ static bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &even return !(Z_TYPE_P(&retval) == IS_FALSE); } +void php_swoole_coroutine_scheduler_rshutdown() { + swoole_name_resolver_each([](const std::list::iterator &iter) -> swTraverseOperation { + if (iter->type == NameResolver::TYPE_PHP) { + zval_dtor((zval *) iter->private_data); + efree(iter->private_data); + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + + if (exit_condition_fci_cache) { + sw_callable_free(exit_condition_fci_cache); + exit_condition_fci_cache = nullptr; + } +} + void php_swoole_set_coroutine_option(zend_array *vht) { zval *ztmp; if (php_swoole_array_get_value(vht, "max_coro_num", ztmp) || - php_swoole_array_get_value(vht, "max_coroutine", ztmp)) { + php_swoole_array_get_value(vht, "max_coroutine", ztmp)) { zend_long max_num = zval_get_long(ztmp); PHPCoroutine::set_max_num(max_num <= 0 ? SW_DEFAULT_MAX_CORO_NUM : max_num); } @@ -156,7 +158,17 @@ void php_swoole_set_coroutine_option(zend_array *vht) { PHPCoroutine::enable_preemptive_scheduler(zval_is_true(ztmp)); } if (php_swoole_array_get_value(vht, "c_stack_size", ztmp) || php_swoole_array_get_value(vht, "stack_size", ztmp)) { - Coroutine::set_stack_size(zval_get_long(ztmp)); + Coroutine::set_stack_size(php_swoole_parse_to_size(ztmp)); + } + if (php_swoole_array_get_value(vht, "name_resolver", ztmp)) { + if (!ZVAL_IS_ARRAY(ztmp)) { + php_swoole_fatal_error(E_WARNING, "name_resolver must be an array"); + } else { + zend_hash_apply(Z_ARR_P(ztmp), [](zval *zresolver) -> int { + php_swoole_name_resolver_add(zresolver); + return ZEND_HASH_APPLY_KEEP; + }); + } } if (PHPCoroutine::options) { zend_hash_merge(PHPCoroutine::options, vht, zval_add_ref, true); @@ -186,33 +198,16 @@ PHP_METHOD(swoole_coroutine_scheduler, set) { } /* Reactor can exit */ if ((ztmp = zend_hash_str_find(vht, ZEND_STRL("exit_condition")))) { - char *func_name; - if (exit_condition_fci_cache.function_handler) { - sw_zend_fci_cache_discard(&exit_condition_fci_cache); - exit_condition_fci_cache.function_handler = nullptr; + if (exit_condition_fci_cache) { + sw_callable_free(exit_condition_fci_cache); } - if (!ZVAL_IS_NULL(ztmp)) { - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, &exit_condition_fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "exit_condition '%s' is not callable", func_name); - } else { - efree(func_name); - sw_zend_fci_cache_persist(&exit_condition_fci_cache); - if (!exit_condition_cleaner) { - php_swoole_register_rshutdown_callback( - [](void *data) { - if (exit_condition_fci_cache.function_handler) { - sw_zend_fci_cache_discard(&exit_condition_fci_cache); - exit_condition_fci_cache.function_handler = nullptr; - } - }, - nullptr); - exit_condition_cleaner = true; - } - SwooleG.user_exit_condition = php_swoole_coroutine_reactor_can_exit; - if (sw_reactor()) { - sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, - SwooleG.user_exit_condition); - } + + exit_condition_fci_cache = sw_callable_create(ztmp); + if (exit_condition_fci_cache) { + SwooleG.user_exit_condition = php_swoole_coroutine_reactor_can_exit; + if (sw_reactor()) { + sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, + SwooleG.user_exit_condition); } } else { if (sw_reactor()) { @@ -282,11 +277,6 @@ static PHP_METHOD(swoole_coroutine_scheduler, parallel) { static PHP_METHOD(swoole_coroutine_scheduler, start) { SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); - if (SwooleTG.reactor) { - php_swoole_fatal_error( - E_WARNING, "eventLoop has already been created. unable to start %s", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } if (s->started) { php_swoole_fatal_error( E_WARNING, "scheduler is started, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); @@ -307,7 +297,7 @@ static PHP_METHOD(swoole_coroutine_scheduler, start) { SchedulerTask *task = s->list->front(); s->list->pop(); for (zend_long i = 0; i < task->count; i++) { - PHPCoroutine::create(&task->fci_cache, task->fci.param_count, task->fci.params); + PHPCoroutine::create(&task->fci_cache, task->fci.param_count, task->fci.params, &task->fci.function_name); } sw_zend_fci_cache_discard(&task->fci_cache); sw_zend_fci_params_discard(&task->fci); diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index ada70e93311..45646bf11a0 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -1,3 +1,19 @@ +/* ++----------------------------------------------------------------------+ +| Swoole | ++----------------------------------------------------------------------+ +| This source file is subject to version 2.0 of the Apache license, | +| that is bundled with this package in the file LICENSE, and is | +| available through the world-wide-web at the following url: | +| http://www.apache.org/licenses/LICENSE-2.0.html | +| If you did not receive a copy of the Apache2.0 license and are unable| +| to obtain it through the world-wide-web, please send a note to | +| license@swoole.com so we can mail you a copy immediately. | ++----------------------------------------------------------------------+ +| Author: Tianfeng Han | ++----------------------------------------------------------------------+ +*/ + #include "php_swoole_coroutine_system.h" #include "ext/standard/file.h" @@ -5,49 +21,40 @@ #include -using swoole::TimerNode; using swoole::Coroutine; +using swoole::Event; using swoole::PHPCoroutine; using swoole::Reactor; -using swoole::Event; +using swoole::String; +using swoole::TimerNode; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::String; static zend_class_entry *swoole_coroutine_system_ce; // clang-format off - static const zend_function_entry swoole_coroutine_system_methods[] = { - ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_swoole_coroutine_system_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_swoole_coroutine_system_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, exec, arginfo_swoole_coroutine_system_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, sleep, arginfo_swoole_coroutine_system_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_swoole_coroutine_system_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, statvfs, arginfo_swoole_coroutine_system_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, readFile, arginfo_swoole_coroutine_system_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, writeFile, arginfo_swoole_coroutine_system_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, wait, arginfo_swoole_coroutine_system_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitPid, arginfo_swoole_coroutine_system_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitSignal, arginfo_swoole_coroutine_system_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitEvent, arginfo_swoole_coroutine_system_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_class_Swoole_Coroutine_System_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, exec, arginfo_class_Swoole_Coroutine_System_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, sleep, arginfo_class_Swoole_Coroutine_System_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_class_Swoole_Coroutine_System_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, statvfs, arginfo_class_Swoole_Coroutine_System_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, readFile, arginfo_class_Swoole_Coroutine_System_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, writeFile, arginfo_class_Swoole_Coroutine_System_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, wait, arginfo_class_Swoole_Coroutine_System_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on void php_swoole_coroutine_system_minit(int module_number) { - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_system, - "Swoole\\Coroutine\\System", - nullptr, - "Co\\System", - swoole_coroutine_system_methods, - nullptr); + SW_INIT_CLASS_ENTRY_BASE( + swoole_coroutine_system, "Swoole\\Coroutine\\System", "Co\\System", swoole_coroutine_system_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_system, sw_zend_create_object_deny); } @@ -65,228 +72,6 @@ PHP_METHOD(swoole_coroutine_system, sleep) { RETURN_BOOL(System::sleep(seconds) == 0); } -static void co_socket_read(int fd, zend_long length, INTERNAL_FUNCTION_PARAMETERS) { - php_swoole_check_reactor(); - Socket _socket(fd, SW_SOCK_RAW); - - zend_string *buf = zend_string_alloc(length + 1, 0); - size_t nbytes = length <= 0 ? SW_BUFFER_SIZE_STD : length; - ssize_t n = _socket.read(ZSTR_VAL(buf), nbytes); - if (n < 0) { - ZVAL_FALSE(return_value); - zend_string_free(buf); - } else if (n == 0) { - ZVAL_EMPTY_STRING(return_value); - zend_string_free(buf); - } else { - ZSTR_VAL(buf)[n] = 0; - ZSTR_LEN(buf) = n; - ZVAL_STR(return_value, buf); - } - _socket.move_fd(); -} - -static void co_socket_write(int fd, char *str, size_t l_str, INTERNAL_FUNCTION_PARAMETERS) { - php_swoole_check_reactor(); - Socket _socket(fd, SW_SOCK_RAW); - - ssize_t n = _socket.write(str, l_str); - if (n < 0) { - swoole_set_last_error(errno); - ZVAL_FALSE(return_value); - } else { - ZVAL_LONG(return_value, n); - } - _socket.move_fd(); -} - -PHP_METHOD(swoole_coroutine_system, fread) { - Coroutine::get_current_safe(); - - zval *handle; - zend_long length = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_RESOURCE(handle) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - - if (async) { - co_socket_read(fd, length, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - - if (length <= 0) { - struct stat file_stat; - if (swoole_coroutine_fstat(fd, &file_stat) < 0) { - swoole_set_last_error(errno); - RETURN_FALSE; - } - off_t _seek = swoole_coroutine_lseek(fd, 0, SEEK_CUR); - if (_seek < 0) { - swoole_set_last_error(errno); - RETURN_FALSE; - } - if (_seek >= file_stat.st_size) { - length = SW_BUFFER_SIZE_STD; - } else { - length = file_stat.st_size - _seek; - } - } - - char *buf = (char *) emalloc(length + 1); - if (!buf) { - RETURN_FALSE; - } - buf[length] = 0; - int ret = -1; - swoole_trace("fd=%d, length=%ld", fd, length); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - while (1) { - ret = read(fd, buf, length); - if (ret < 0 && errno == EINTR) { - continue; - } - break; - } - }); - - if (async_success && ret >= 0) { - // TODO: Optimization: reduce memory copy - ZVAL_STRINGL(return_value, buf, ret); - } else { - ZVAL_FALSE(return_value); - } - - efree(buf); -} - -PHP_METHOD(swoole_coroutine_system, fgets) { - Coroutine::get_current_safe(); - - zval *handle; - php_stream *stream; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_RESOURCE(handle) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - - if (async == 1) { - php_swoole_fatal_error(E_WARNING, "only support file resources"); - RETURN_FALSE; - } - - php_stream_from_res(stream, Z_RES_P(handle)); - - FILE *file; - if (stream->stdiocast) { - file = stream->stdiocast; - } else { - if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void **) &file, 1) != SUCCESS || file == nullptr) { - RETURN_FALSE; - } - } - - if (stream->readbuf == nullptr) { - stream->readbuflen = stream->chunk_size; - stream->readbuf = (uchar *) emalloc(stream->chunk_size); - } - - if (!stream->readbuf) { - RETURN_FALSE; - } - - int ret = 0; - swoole_trace("fd=%d, length=%ld", fd, stream->readbuflen); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - char *data = fgets((char *) stream->readbuf, stream->readbuflen, file); - if (data == nullptr) { - ret = -1; - stream->eof = 1; - } - }); - - if (async_success && ret != -1) { - ZVAL_STRING(return_value, (char *) stream->readbuf); - } else { - ZVAL_FALSE(return_value); - } -} - -PHP_METHOD(swoole_coroutine_system, fwrite) { - Coroutine::get_current_safe(); - - zval *handle; - char *str; - size_t l_str; - zend_long length = 0; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_RESOURCE(handle) - Z_PARAM_STRING(str, l_str) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - - if (async) { - co_socket_write( - fd, str, (length <= 0 || (size_t) length > l_str) ? l_str : length, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - - if (length <= 0 || (size_t) length > l_str) { - length = l_str; - } - - char *buf = estrndup(str, length); - - if (!buf) { - RETURN_FALSE; - } - - int ret = -1; - swoole_trace("fd=%d, length=%ld", fd, length); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - while (1) { - ret = write(fd, buf, length); - if (ret < 0 && errno == EINTR) { - continue; - } - break; - } - }); - - if (async_success && ret >= 0) { - ZVAL_LONG(return_value, ret); - } else { - ZVAL_FALSE(return_value); - } - - efree(buf); -} - PHP_METHOD(swoole_coroutine_system, readFile) { char *filename; size_t l_filename; @@ -343,9 +128,12 @@ PHP_FUNCTION(swoole_coroutine_gethostbyname) { zend_long family = AF_INET; double timeout = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ld", &domain_name, &l_domain_name, &family, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(domain_name, l_domain_name) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_domain_name == 0) { php_swoole_fatal_error(E_WARNING, "domain name is empty"); @@ -379,18 +167,15 @@ PHP_METHOD(swoole_coroutine_system, getaddrinfo) { size_t l_service = 0; double timeout = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "s|lllsd", - &hostname, - &l_hostname, - &family, - &socktype, - &protocol, - &service, - &l_service, - &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 6) + Z_PARAM_STRING(hostname, l_hostname) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_LONG(socktype) + Z_PARAM_LONG(protocol) + Z_PARAM_STRING(service, l_service) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_hostname == 0) { php_swoole_fatal_error(E_WARNING, "hostname is empty"); @@ -451,56 +236,24 @@ PHP_METHOD(swoole_coroutine_system, exec) { Z_PARAM_BOOL(get_error_stream) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_signal_isset_handler(SIGCHLD)) { - php_swoole_error(E_WARNING, "The signal [SIGCHLD] is registered, cannot execute swoole_coroutine_exec"); + int status; + auto buffer = std::shared_ptr(swoole::make_string(1024, sw_zend_string_allocator())); + if (!System::exec(command, get_error_stream, buffer, &status)) { RETURN_FALSE; } - Coroutine::get_current_safe(); - - pid_t pid; - int fd = swoole_shell_exec(command, &pid, get_error_stream); - if (fd < 0) { - php_swoole_error(E_WARNING, "Unable to execute '%s'", command); - RETURN_FALSE; - } - - String *buffer = new String(1024); - Socket socket(fd, SW_SOCK_UNIX_STREAM); - while (1) { - ssize_t retval = socket.read(buffer->str + buffer->length, buffer->size - buffer->length); - if (retval > 0) { - buffer->length += retval; - if (buffer->length == buffer->size) { - if (!buffer->extend()) { - break; - } - } - } else { - break; - } - } - socket.close(); + auto str = zend::fetch_zend_string_by_val(buffer->str); + buffer->set_null_terminated(); + str->len = buffer->length; + buffer->release(); zval zdata; - if (buffer->length == 0) { - ZVAL_EMPTY_STRING(&zdata); - } else { - ZVAL_STRINGL(&zdata, buffer->str, buffer->length); - } - delete buffer; + ZVAL_STR(&zdata, str); - int status; - pid_t _pid = swoole_coroutine_waitpid(pid, &status, 0); - if (_pid > 0) { - array_init(return_value); - add_assoc_long(return_value, "code", WEXITSTATUS(status)); - add_assoc_long(return_value, "signal", WTERMSIG(status)); - add_assoc_zval(return_value, "output", &zdata); - } else { - zval_ptr_dtor(&zdata); - RETVAL_FALSE; - } + array_init(return_value); + add_assoc_long(return_value, "code", WEXITSTATUS(status)); + add_assoc_long(return_value, "signal", WTERMSIG(status)); + add_assoc_zval(return_value, "output", &zdata); } static void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid, double timeout) { @@ -525,6 +278,7 @@ static void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid } PHP_METHOD(swoole_coroutine_system, wait) { + SW_MUST_BE_MAIN_THREAD(); double timeout = -1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -536,6 +290,7 @@ PHP_METHOD(swoole_coroutine_system, wait) { } PHP_METHOD(swoole_coroutine_system, waitPid) { + SW_MUST_BE_MAIN_THREAD(); zend_long pid; double timeout = -1; @@ -549,26 +304,40 @@ PHP_METHOD(swoole_coroutine_system, waitPid) { } PHP_METHOD(swoole_coroutine_system, waitSignal) { - zend_long signo; + SW_MUST_BE_MAIN_THREAD(); + zval *zsignals; double timeout = -1; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(signo) + Z_PARAM_ZVAL(zsignals) Z_PARAM_OPTIONAL Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (!System::wait_signal(signo, timeout)) { + std::vector signals; + + if (ZVAL_IS_ARRAY(zsignals)) { + zval *item; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zsignals), item) { + signals.push_back(zval_get_long(item)); + } + ZEND_HASH_FOREACH_END(); + } else { + signals.push_back(zval_get_long(zsignals)); + } + + int signo = System::wait_signal(signals, timeout); + if (signo == -1) { if (swoole_get_last_error() == EBUSY) { php_swoole_fatal_error(E_WARNING, "Unable to wait signal, async signal listener has been registered"); } else if (swoole_get_last_error() == EINVAL) { - php_swoole_fatal_error(E_WARNING, "Invalid signal [" ZEND_LONG_FMT "]", signo); + php_swoole_fatal_error(E_WARNING, "Invalid signal in the given list"); } errno = swoole_get_last_error(); RETURN_FALSE; } - RETURN_TRUE; + RETURN_LONG(signo); } PHP_METHOD(swoole_coroutine_system, waitEvent) { diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index 49af3ce35a3..85b8deefe59 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,14 +22,71 @@ namespace swoole { namespace curl { +static SW_THREAD_LOCAL std::unordered_map handle_buckets; + +Handle *get_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + return iter == handle_buckets.end() ? nullptr : iter->second; +} + +Handle *create_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + if (iter != handle_buckets.end()) { + return nullptr; + } + Handle *handle = new Handle(cp); + handle_buckets[cp] = handle; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_MAGENTA " handle=%p, curl=%p", "[CREATE]", handle, cp); + return handle; +} + +void destroy_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + if (iter == handle_buckets.end()) { + return; + } + auto handle = iter->second; + handle_buckets.erase(iter); + delete handle; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[DESTROY]", handle, cp); +} + static int execute_callback(Event *event, int bitmask) { Handle *handle = (Handle *) event->socket->object; - handle->event_bitmask |= bitmask; - handle->event_fd = event->fd; - handle->multi->callback(handle, bitmask); + auto it = handle->sockets.find(event->fd); + if (it != handle->sockets.end()) { + it->second->event_bitmask |= bitmask; + it->second->event_fd = event->fd; + } + handle->multi->callback(handle, bitmask, event->fd); return 0; } +void Handle::destroy_socket(curl_socket_t sockfd) { + auto it = sockets.find(sockfd); + if (it != sockets.end()) { + auto _socket = it->second; + sockets.erase(it); + _socket->socket->fd = -1; + _socket->socket->free(); + delete _socket; + } +} + +HandleSocket *Handle::create_socket(curl_socket_t sockfd) { + auto socket = new network::Socket(); + socket->fd = sockfd; + socket->removed = 1; + socket->fd_type = (FdType) PHP_SWOOLE_FD_CO_CURL; + + HandleSocket *handle_socket = new HandleSocket(); + handle_socket->socket = socket; + sockets[sockfd] = handle_socket; + socket->object = this; + + return handle_socket; +} + int Multi::cb_readable(Reactor *reactor, Event *event) { return execute_callback(event, CURL_CSELECT_IN); } @@ -42,18 +99,19 @@ int Multi::cb_error(Reactor *reactor, Event *event) { return execute_callback(event, CURL_CSELECT_ERR); } -int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) { +int Multi::handle_socket(CURL *easy, curl_socket_t sockfd, int action, void *userp, void *socketp) { Multi *multi = (Multi *) userp; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_CYAN "action=%d, userp=%p, socketp=%p", "[HANDLE_SOCKET]", action, userp, socketp); + swoole_trace_log( + SW_TRACE_CO_CURL, SW_ECHO_CYAN "action=%d, userp=%p, socketp=%p", "[HANDLE_SOCKET]", action, userp, socketp); switch (action) { case CURL_POLL_IN: case CURL_POLL_OUT: case CURL_POLL_INOUT: - multi->set_event(easy, socketp, s, action); + multi->set_event(easy, socketp, sockfd, action); break; case CURL_POLL_REMOVE: if (socketp) { - multi->del_event(easy, socketp, s); + multi->del_event(easy, socketp, sockfd); } break; default: @@ -62,44 +120,45 @@ int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, v return 0; } -Socket *Multi::create_socket(CURL *cp, curl_socket_t sockfd) { +HandleSocket *Multi::create_socket(Handle *handle, curl_socket_t sockfd) { if (!swoole_event_isset_handler(PHP_SWOOLE_FD_CO_CURL)) { swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_READ, cb_readable); swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_WRITE, cb_writable); swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_ERROR, cb_error); } - Socket *socket = new Socket(); - socket->fd = sockfd; - socket->removed = 1; - socket->fd_type = (FdType) PHP_SWOOLE_FD_CO_CURL; - curl_multi_assign(multi_handle_, sockfd, (void *) socket); - Handle *handle = get_handle(cp); - handle->socket = socket; - handle->cp = cp; - socket->object = handle; + auto _socket = handle->create_socket(sockfd); + if (curl_multi_assign(multi_handle_, sockfd, (void *) _socket) != CURLM_OK) { + handle->destroy_socket(sockfd); + return nullptr; + } - return socket; + return _socket; } void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { - Socket *socket = (Socket *) socket_ptr; - socket->silent_remove = 1; - if (socket->events && swoole_event_is_available() && swoole_event_del(socket) == SW_OK) { + HandleSocket *curl_socket = (HandleSocket *) socket_ptr; + curl_socket->socket->silent_remove = 1; + if (curl_socket->socket->events && swoole_event_is_available() && swoole_event_del(curl_socket->socket) == SW_OK) { event_count_--; } - socket->fd = -1; - socket->free(); curl_multi_assign(multi_handle_, sockfd, NULL); Handle *handle = get_handle(cp); - handle->socket = nullptr; + if (handle) { + handle->destroy_socket(sockfd); + } swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL_EVENT]", handle, cp, sockfd); } void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int action) { - Socket *socket = socket_ptr ? (Socket *) socket_ptr : create_socket(cp, sockfd); + auto handle = get_handle(cp); + if (!handle) { + return; + } + + HandleSocket *curl_socket = socket_ptr ? (HandleSocket *) socket_ptr : create_socket(handle, sockfd); int events = 0; if (action != CURL_POLL_IN) { events |= SW_EVENT_WRITE; @@ -107,66 +166,70 @@ void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int acti if (action != CURL_POLL_OUT) { events |= SW_EVENT_READ; } - assert(socket->fd > 0); - socket->fd = sockfd; - if (socket->events) { - swoole_event_set(socket, events); + assert(curl_socket->socket->fd > 0); + curl_socket->socket->fd = sockfd; + if (curl_socket->socket->events) { + swoole_event_set(curl_socket->socket, events); } else { - if (swoole_event_add(socket, events) == SW_OK) { + if (swoole_event_add(curl_socket->socket, events) == SW_OK) { event_count_++; } } - Handle *handle = get_handle(cp); - handle->action = action; - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", "[ADD_EVENT]", handle, cp, sockfd, events); + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + it->second->action = action; + } + + swoole_trace_log(SW_TRACE_CO_CURL, + SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", + "[ADD_EVENT]", + handle, + cp, + sockfd, + events); } -CURLMcode Multi::add_handle(CURL *cp) { - auto retval = curl_multi_add_handle(multi_handle_, cp); +CURLMcode Multi::add_handle(Handle *handle) { + if (handle == nullptr) { + php_swoole_fatal_error(E_WARNING, "The given handle is not initialized in coroutine"); + return CURLM_INTERNAL_ERROR; + } + auto retval = curl_multi_add_handle(multi_handle_, handle->cp); if (retval == CURLM_OK) { - auto handle = get_handle(cp); - if (handle == nullptr) { - handle = new Handle{}; - handle->cp = cp; - curl_easy_setopt(cp, CURLOPT_PRIVATE, handle); - } handle->multi = this; - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, cp); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, handle->cp); } return retval; } -CURLMcode Multi::remove_handle(CURL *cp) { - auto retval = curl_multi_remove_handle(multi_handle_, cp); - if (retval == CURLM_OK) { - auto handle = get_handle(cp); - if (handle) { - handle->multi = nullptr; - } - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, cp); - } - return retval; +CURLMcode Multi::remove_handle(Handle *handle) { + handle->multi = nullptr; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, handle->cp); + return curl_multi_remove_handle(multi_handle_, handle->cp); } -CURLcode Multi::exec(php_curl *ch) { - if (add_handle(ch->cp) != CURLM_OK) { +CURLcode Multi::exec(Handle *handle) { + if (add_handle(handle) != CURLM_OK) { return CURLE_FAILED_INIT; } - Handle *handle = get_handle(ch->cp); + HandleSocket *curl_socket = nullptr; bool is_canceled = false; SW_LOOP { - if (handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + for (auto it : handle->sockets) { + curl_socket = it.second; + if (curl_socket->socket && curl_socket->socket->removed) { + if (swoole_event_add(curl_socket->socket, get_event(curl_socket->action)) == SW_OK) { + event_count_++; + } + swoole_trace_log(SW_TRACE_CO_CURL, + "resume, handle=%p, curl=%p, fd=%d", + handle, + handle->cp, + curl_socket->socket->get_fd()); } - swoole_trace_log( - SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); } co = check_bound_co(); @@ -182,9 +245,13 @@ CURLcode Multi::exec(php_curl *ch) { int sockfd = last_sockfd; int bitmask = 0; if (sockfd >= 0) { - bitmask = handle->event_bitmask; - if (handle->socket && !handle->socket->removed && swoole_event_del(handle->socket) == SW_OK) { - event_count_--; + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + curl_socket = it->second; + bitmask = curl_socket->event_bitmask; + if (!curl_socket->socket->removed && swoole_event_del(curl_socket->socket) == SW_OK) { + event_count_--; + } } } del_timer(); @@ -200,18 +267,41 @@ CURLcode Multi::exec(php_curl *ch) { break; } set_timer(); - if (sockfd >= 0 && handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + if (sockfd >= 0) { + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + curl_socket = it->second; + if (curl_socket->socket && curl_socket->socket->removed) { + if (swoole_event_add(curl_socket->socket, get_event(curl_socket->action)) == SW_OK) { + event_count_++; + } + } } } - if (!timer && handle->socket->removed) { - break; + + if (!timer) { + bool removed = true; + for (auto it = handle->sockets.begin(); it != handle->sockets.end();) { + curl_socket = it->second; + if (curl_socket->socket) { + if (curl_socket->socket->removed) { + it = handle->sockets.erase(it); + delete curl_socket; + continue; + } else { + removed = false; + } + } + ++it; + } + if (removed) { + break; + } } } CURLcode retval = read_info(); - remove_handle(ch->cp); + remove_handle(handle); return is_canceled ? CURLE_ABORTED_BY_CALLBACK : retval; } @@ -238,12 +328,16 @@ CURLcode Multi::read_info() { int Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) { Multi *multi = (Multi *) userp; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE "timeout_ms=%d", "[HANDLE_TIMEOUT]", timeout_ms); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE " timeout_ms=%ld", "[HANDLE_TIMEOUT]", timeout_ms); if (!swoole_event_is_available()) { - return 0; + return -1; } if (timeout_ms < 0) { - multi->del_timer(); + if (multi->timer) { + multi->del_timer(); + } else { + multi->add_timer(1000); + } } else { if (timeout_ms == 0) { timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ @@ -262,6 +356,8 @@ long Multi::select(php_curlm *mh, double timeout) { return CURLE_FAILED_INIT; } + network::Socket *socket = nullptr; + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { zval *z_ch = (zval *) element->data; php_curl *ch; @@ -270,14 +366,24 @@ long Multi::select(php_curlm *mh, double timeout) { } Handle *handle = get_handle(ch->cp); - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, handle->socket=%p, handle->socket->removed=%d", handle, handle ? handle->socket :nullptr); - - if (handle && handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + if (handle) { + for (auto it : handle->sockets) { + socket = it.second->socket; + + swoole_trace_log(SW_TRACE_CO_CURL, + "handle=%p, socket=%p, socket->removed=%d", + handle, + socket, + socket ? socket->removed : 0); + + if (socket && socket->removed) { + if (swoole_event_add(socket, get_event(it.second->action)) == SW_OK) { + event_count_++; + } + swoole_trace_log( + SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, socket->get_fd()); + } } - swoole_trace_log( - SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); } } set_timer(); @@ -291,7 +397,7 @@ long Multi::select(php_curlm *mh, double timeout) { co->yield_ex(timeout); co = nullptr; - swoole_trace_log(SW_TRACE_CO_CURL, "yield timeout, count=%d", zend_llist_count(&mh->easyh)); + swoole_trace_log(SW_TRACE_CO_CURL, "yield timeout, count=%lu", zend_llist_count(&mh->easyh)); auto count = selector->active_handles.size(); @@ -302,10 +408,15 @@ long Multi::select(php_curlm *mh, double timeout) { continue; } Handle *handle = get_handle(ch->cp); - if (handle && handle->socket && !handle->socket->removed && swoole_event_del(handle->socket) == SW_OK) { - swoole_trace_log( - SW_TRACE_CO_CURL, "suspend, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); - event_count_--; + if (handle) { + for (auto it : handle->sockets) { + socket = it.second->socket; + if (socket && !socket->removed && swoole_event_del(socket) == SW_OK) { + swoole_trace_log( + SW_TRACE_CO_CURL, "suspend, handle=%p, curl=%p, fd=%d", handle, ch->cp, socket->get_fd()); + event_count_--; + } + } } } del_timer(); @@ -318,8 +429,15 @@ long Multi::select(php_curlm *mh, double timeout) { for (auto iter = selector->active_handles.begin(); iter != selector->active_handles.end(); iter++) { Handle *handle = *iter; - curl_multi_socket_action(multi_handle_, handle->event_fd, handle->event_bitmask, &running_handles_); - swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[socket], running_handles=%d", running_handles_); + if (handle) { + for (auto it = handle->sockets.begin(); it != handle->sockets.end();) { + HandleSocket *handle_socket = it->second; + it++; + curl_multi_socket_action( + multi_handle_, handle_socket->event_fd, handle_socket->event_bitmask, &running_handles_); + swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[socket], running_handles=%d", running_handles_); + } + } } selector->active_handles.clear(); @@ -327,10 +445,11 @@ long Multi::select(php_curlm *mh, double timeout) { return count; } -void Multi::callback(Handle *handle, int event_bitmask) { - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p", handle, event_bitmask, co); +void Multi::callback(Handle *handle, int event_bitmask, int sockfd) { + swoole_trace_log( + SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p, sockfd=%d", handle, event_bitmask, co, sockfd); if (handle) { - last_sockfd = handle->event_fd; + last_sockfd = sockfd; } else { last_sockfd = -1; } @@ -341,8 +460,10 @@ void Multi::callback(Handle *handle, int event_bitmask) { } if (!co) { if (handle) { - if (swoole_event_del(handle->socket) == SW_OK) { - event_count_--; + for (auto it : handle->sockets) { + if (swoole_event_del(it.second->socket) == SW_OK) { + event_count_--; + } } } else { del_timer(); @@ -367,4 +488,26 @@ void Multi::callback(Handle *handle, int event_bitmask) { } } // namespace curl } // namespace swoole + +CURLcode swoole_curl_easy_perform(CURL *cp) { + Multi *multi = new Multi(); + CURLcode error = multi->exec(swoole::curl::get_handle(cp)); + delete multi; + return error; +} + +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { + php_curl *ch = Z_CURL_P(zid); + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { + exclusive = false; + } + if (exclusive && swoole_coroutine_is_in()) { + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { + return nullptr; + } + } + return ch; +} + #endif diff --git a/ext-src/swoole_curl_interface.h b/ext-src/swoole_curl_interface.h new file mode 100644 index 00000000000..fa93e061a86 --- /dev/null +++ b/ext-src/swoole_curl_interface.h @@ -0,0 +1,46 @@ +#pragma once + +#include "php_swoole_cxx.h" + +#ifdef SW_USE_CURL +SW_EXTERN_C_BEGIN + +#include +#include + +void swoole_native_curl_minit(int module_number); +void swoole_native_curl_mshutdown(); + +PHP_FUNCTION(swoole_native_curl_close); +PHP_FUNCTION(swoole_native_curl_copy_handle); +PHP_FUNCTION(swoole_native_curl_errno); +PHP_FUNCTION(swoole_native_curl_error); +PHP_FUNCTION(swoole_native_curl_exec); +PHP_FUNCTION(swoole_native_curl_getinfo); +PHP_FUNCTION(swoole_native_curl_init); +PHP_FUNCTION(swoole_native_curl_setopt); +PHP_FUNCTION(swoole_native_curl_setopt_array); +PHP_FUNCTION(swoole_native_curl_reset); +PHP_FUNCTION(swoole_native_curl_escape); +PHP_FUNCTION(swoole_native_curl_unescape); +PHP_FUNCTION(swoole_native_curl_pause); +PHP_FUNCTION(swoole_native_curl_multi_add_handle); +PHP_FUNCTION(swoole_native_curl_multi_close); +PHP_FUNCTION(swoole_native_curl_multi_errno); +PHP_FUNCTION(swoole_native_curl_multi_exec); +PHP_FUNCTION(swoole_native_curl_multi_select); +PHP_FUNCTION(swoole_native_curl_multi_remove_handle); +PHP_FUNCTION(swoole_native_curl_multi_setopt); +PHP_FUNCTION(swoole_native_curl_multi_getcontent); +PHP_FUNCTION(swoole_native_curl_multi_info_read); +PHP_FUNCTION(swoole_native_curl_multi_init); + +#if PHP_VERSION_ID >= 80400 +PHP_FUNCTION(swoole_native_curl_upkeep); +PHP_FUNCTION(swoole_native_curl_version); +PHP_FUNCTION(swoole_native_curl_strerror); +PHP_FUNCTION(swoole_native_curl_multi_strerror); +#endif + +SW_EXTERN_C_END +#endif diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index df72baa30f8..e5960ccccc3 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -20,6 +20,10 @@ #include "swoole_server.h" #include "swoole_signal.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_event_arginfo.h" +END_EXTERN_C() + using namespace swoole; using swoole::network::Socket; @@ -30,8 +34,8 @@ static zend_object_handlers swoole_event_handlers; struct EventObject { zval zsocket; - zend_fcall_info_cache fci_cache_read; - zend_fcall_info_cache fci_cache_write; + zend::Callable *readable_callback; + zend::Callable *writable_callback; }; static int event_readable_callback(Reactor *reactor, Event *event); @@ -55,100 +59,90 @@ static PHP_FUNCTION(swoole_event_isset); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_add, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_set, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_write, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_defer, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_cycle, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 1) - ZEND_ARG_INFO(0, before) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_del, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_isset, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_event_methods[] = { - ZEND_FENTRY(add, ZEND_FN(swoole_event_add), arginfo_swoole_event_add, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(del, ZEND_FN(swoole_event_del), arginfo_swoole_event_del, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(set, ZEND_FN(swoole_event_set), arginfo_swoole_event_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(isset, ZEND_FN(swoole_event_isset), arginfo_swoole_event_isset, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dispatch, ZEND_FN(swoole_event_dispatch), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(defer, ZEND_FN(swoole_event_defer), arginfo_swoole_event_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(cycle, ZEND_FN(swoole_event_cycle), arginfo_swoole_event_cycle, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(write, ZEND_FN(swoole_event_write), arginfo_swoole_event_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(wait, ZEND_FN(swoole_event_wait), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(exit, ZEND_FN(swoole_event_exit), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(add, ZEND_FN(swoole_event_add), arginfo_swoole_event_add, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(del, ZEND_FN(swoole_event_del), arginfo_swoole_event_del, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(set, ZEND_FN(swoole_event_set), arginfo_swoole_event_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(isset, ZEND_FN(swoole_event_isset), arginfo_swoole_event_isset, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dispatch, ZEND_FN(swoole_event_dispatch), arginfo_swoole_event_dispatch, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(defer, ZEND_FN(swoole_event_defer), arginfo_swoole_event_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(cycle, ZEND_FN(swoole_event_cycle), arginfo_swoole_event_cycle, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(write, ZEND_FN(swoole_event_write), arginfo_swoole_event_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(wait, ZEND_FN(swoole_event_wait), arginfo_swoole_event_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_event_rshutdown, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(exit, ZEND_FN(swoole_event_exit), arginfo_swoole_event_exit, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on void php_swoole_event_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", "swoole_event", nullptr, swoole_event_methods); + SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", nullptr, swoole_event_methods); SW_SET_CLASS_CREATE(swoole_event, sw_zend_create_object_deny); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "del", CG(function_table), "swoole_event_del"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "set", CG(function_table), "swoole_event_set"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "isset", CG(function_table), "swoole_event_isset"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "dispatch", CG(function_table), "swoole_event_dispatch"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", CG(function_table), "swoole_event_defer"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "cycle", CG(function_table), "swoole_event_cycle"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "write", CG(function_table), "swoole_event_write"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "wait", CG(function_table), "swoole_event_wait"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "exit", CG(function_table), "swoole_event_exit"); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add", arginfo_swoole_event_add); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "del", CG(function_table), "swoole_event_del", arginfo_swoole_event_del); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "set", CG(function_table), "swoole_event_set", arginfo_swoole_event_set); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "wait", CG(function_table), "swoole_event_wait", arginfo_swoole_event_wait); + + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "isset", + CG(function_table), + "swoole_event_isset", + arginfo_swoole_event_isset); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "dispatch", + CG(function_table), + "swoole_event_dispatch", + arginfo_swoole_event_dispatch); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "defer", + CG(function_table), + "swoole_event_defer", + arginfo_swoole_event_defer); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "cycle", + CG(function_table), + "swoole_event_cycle", + arginfo_swoole_event_cycle); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "write", + CG(function_table), + "swoole_event_write", + arginfo_swoole_event_write); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "exit", + CG(function_table), + "swoole_event_exit", + arginfo_swoole_event_rshutdown); } static void event_object_free(void *data) { EventObject *peo = (EventObject *) data; - if (peo->fci_cache_read.function_handler) { - sw_zend_fci_cache_discard(&peo->fci_cache_read); + if (peo->readable_callback) { + delete peo->readable_callback; } - if (peo->fci_cache_write.function_handler) { - sw_zend_fci_cache_discard(&peo->fci_cache_write); + if (peo->writable_callback) { + delete peo->writable_callback; } zval_ptr_dtor((&peo->zsocket)); efree(peo); } -static int event_readable_callback(Reactor *reactor, swEvent *event) { +static int event_readable_callback(Reactor *reactor, Event *event) { EventObject *peo = (EventObject *) event->socket->object; zval argv[1]; argv[0] = peo->zsocket; + auto fcc = peo->readable_callback->ptr(); - if (UNEXPECTED(!zend::function::call(&peo->fci_cache_read, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, - "%s: onRead callback handler error, fd [%d] will be removed from reactor", + "%s: readable callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), php_swoole_convert_to_fd(&peo->zsocket)); event->socket->object = nullptr; @@ -165,10 +159,11 @@ static int event_writable_callback(Reactor *reactor, Event *event) { zval argv[1]; argv[0] = peo->zsocket; + auto fcc = peo->writable_callback->ptr(); - if (UNEXPECTED(!zend::function::call(&peo->fci_cache_write, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, - "%s: onWrite callback handler error, fd [%d] will be removed from reactor", + "%s: writable callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), php_swoole_convert_to_fd(&peo->zsocket)); event->socket->object = nullptr; @@ -180,7 +175,7 @@ static int event_writable_callback(Reactor *reactor, Event *event) { return SW_OK; } -static int event_error_callback(Reactor *reactor, swEvent *event) { +static int event_error_callback(Reactor *reactor, Event *event) { if (!(event->socket->events & SW_EVENT_ERROR)) { if (event->socket->events & SW_EVENT_READ) { return reactor->get_handler(SW_EVENT_READ, event->socket->fd_type)(reactor, event); @@ -206,19 +201,16 @@ static int event_error_callback(Reactor *reactor, swEvent *event) { } static void event_defer_callback(void *data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - - if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + zend::Callable *cb = (zend::Callable *) data; + if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::defer callback handler error", ZSTR_VAL(swoole_event_ce->name)); } - - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + delete cb; } static void event_end_callback(void *data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + zend::Callable *cb = (zend::Callable *) data; + if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::end callback handler error", ZSTR_VAL(swoole_event_ce->name)); } } @@ -263,11 +255,10 @@ void php_swoole_event_wait() { if (php_swoole_is_fatal_error() || !sw_reactor()) { return; } -#ifdef HAVE_SIGNALFD - if (sw_reactor()->check_signalfd) { - swoole_signalfd_setup(sw_reactor()); + if (swoole_coroutine_is_in()) { + php_swoole_fatal_error(E_ERROR, "Unable to call Event::wait() in coroutine"); + return; } -#endif if (!sw_reactor()->if_exit() && !sw_reactor()->bailout) { // Don't disable object slot reuse while running shutdown functions: // https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac @@ -307,15 +298,6 @@ int php_swoole_convert_to_fd(zval *zsocket) { return fd; } } -#ifdef SWOOLE_SOCKETS_SUPPORT - else { - php_socket *php_sock; - if ((php_sock = SW_Z_SOCKET_P(zsocket))) { - fd = php_sock->bsd_socket; - return fd; - } - } -#endif php_swoole_fatal_error(E_WARNING, "fd argument must be either valid PHP stream or valid PHP socket resource"); return SW_ERR; } @@ -329,12 +311,21 @@ int php_swoole_convert_to_fd(zval *zsocket) { } case IS_OBJECT: { zval *zfd = nullptr; - if (instanceof_function(Z_OBJCE_P(zsocket), swoole_socket_coro_ce)) { + if (sw_zval_is_co_socket(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_FD), 0); - } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_client_ce)) { + } else if (sw_zval_is_client(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_SOCK), 0); - } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_process_ce)) { + } else if (sw_zval_is_process(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPE), 0); +#ifdef SWOOLE_SOCKETS_SUPPORT + } else if (sw_zval_is_php_socket(zsocket)) { + php_socket *php_sock = SW_Z_SOCKET_P(zsocket); + if (IS_INVALID_SOCKET(php_sock)) { + php_swoole_fatal_error(E_WARNING, "contains a closed socket"); + return SW_ERR; + } + return php_sock->bsd_socket; +#endif } if (zfd == nullptr || Z_TYPE_P(zfd) != IS_LONG) { return SW_ERR; @@ -379,37 +370,10 @@ int php_swoole_convert_to_fd_ex(zval *zsocket, int *async) { #ifdef SWOOLE_SOCKETS_SUPPORT php_socket *php_swoole_convert_to_socket(int sock) { php_socket *socket_object; -#if PHP_VERSION_ID < 80000 - socket_object = (php_socket *) emalloc(sizeof *socket_object); - sw_memset_zero(socket_object, sizeof(*socket_object)); - socket_object->bsd_socket = sock; - socket_object->blocking = 1; - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - - if (getsockname(sock, (struct sockaddr *) &addr, &addr_len) == 0) { - socket_object->type = addr.ss_family; - } else { - php_swoole_sys_error(E_WARNING, "unable to obtain socket family"); - _error: - efree(socket_object); - return nullptr; - } - - int t = fcntl(sock, F_GETFL); - if (t == -1) { - php_swoole_sys_error(E_WARNING, "unable to obtain blocking state"); - goto _error; - } else { - socket_object->blocking = !(t & O_NONBLOCK); - } -#else zval zsocket; object_init_ex(&zsocket, socket_ce); socket_object = Z_SOCKET_P(&zsocket); socket_import_file_descriptor(sock, socket_object); -#endif return socket_object; } #endif @@ -434,24 +398,19 @@ static Socket *event_get_socket(int socket_fd) { static PHP_FUNCTION(swoole_event_add) { zval *zfd; - zend_fcall_info fci_read = empty_fcall_info; - zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache; - zend_fcall_info fci_write = empty_fcall_info; - zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache; zend_long events = SW_EVENT_READ; + zval *zreadable_callback = nullptr; + zval *zwritable_callback = nullptr; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_ZVAL(zfd) Z_PARAM_OPTIONAL - Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0) - Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0) + Z_PARAM_ZVAL(zreadable_callback) + Z_PARAM_ZVAL(zwritable_callback) Z_PARAM_LONG(events) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (fci_read.size == 0 && fci_write.size == 0) { - php_swoole_fatal_error(E_WARNING, "both read and write callbacks are empty"); - RETURN_FALSE; - } + event_check_reactor(); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { @@ -470,28 +429,35 @@ static PHP_FUNCTION(swoole_event_add) { php_swoole_fatal_error(E_WARNING, "invalid events"); RETURN_FALSE; } - - EventObject *peo = (EventObject *) ecalloc(1, sizeof(*peo)); - - Z_TRY_ADDREF_P(zfd); - peo->zsocket = *zfd; - - if (fci_read.size != 0) { - sw_zend_fci_cache_persist(&fci_cache_read); - peo->fci_cache_read = fci_cache_read; - } - if (fci_write.size != 0) { - sw_zend_fci_cache_persist(&fci_cache_write); - peo->fci_cache_write = fci_cache_write; + Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER); + if (!socket) { + RETURN_FALSE; } - event_check_reactor(); + auto readable_callback = sw_callable_create_ex(zreadable_callback, "readable_callback", true); + if ((events & SW_EVENT_READ) && readable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + RETURN_FALSE; + } - Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER); - if (!socket) { + auto writable_callback = sw_callable_create_ex(zwritable_callback, "writable_callback", true); + if ((events & SW_EVENT_WRITE) && writable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + if (readable_callback) { + delete readable_callback; + } RETURN_FALSE; } + EventObject *peo = (EventObject *) ecalloc(1, sizeof(*peo)); + + Z_TRY_ADDREF_P(zfd); + peo->zsocket = *zfd; + peo->readable_callback = readable_callback; + peo->writable_callback = writable_callback; + socket->set_nonblock(); socket->object = peo; @@ -512,9 +478,10 @@ static PHP_FUNCTION(swoole_event_write) { char *data; size_t len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &zfd, &data, &len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zfd) + Z_PARAM_STRING(data, len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (len == 0) { php_swoole_fatal_error(E_WARNING, "data empty"); @@ -548,23 +515,20 @@ static PHP_FUNCTION(swoole_event_set) { } zval *zfd; - zend_fcall_info fci_read = empty_fcall_info; - zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache; - zend_fcall_info fci_write = empty_fcall_info; - zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache; zend_long events = 0; + zval *zreadable_callback = nullptr; + zval *zwritable_callback = nullptr; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_ZVAL(zfd) Z_PARAM_OPTIONAL - Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0) - Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0) + Z_PARAM_ZVAL(zreadable_callback) + Z_PARAM_ZVAL(zwritable_callback) Z_PARAM_LONG(events) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { - php_swoole_fatal_error(E_WARNING, "unknown type"); RETURN_FALSE; } @@ -574,30 +538,29 @@ static PHP_FUNCTION(swoole_event_set) { RETURN_FALSE; } - EventObject *reactor_fd = (EventObject *) socket->object; - if (fci_read.size != 0) { - if (reactor_fd->fci_cache_read.function_handler) { - sw_zend_fci_cache_discard(&reactor_fd->fci_cache_read); + EventObject *peo = (EventObject *) socket->object; + auto readable_callback = sw_callable_create_ex(zreadable_callback, "readable_callback"); + auto writable_callback = sw_callable_create_ex(zwritable_callback, "writable_callback"); + if (readable_callback) { + if (peo->readable_callback) { + swoole_event_defer(sw_callable_free, peo->readable_callback); } - sw_zend_fci_cache_persist(&fci_cache_read); - reactor_fd->fci_cache_read = fci_cache_read; + peo->readable_callback = readable_callback; } - if (fci_write.size != 0) { - if (reactor_fd->fci_cache_write.function_handler) { - sw_zend_fci_cache_discard(&reactor_fd->fci_cache_write); + if (writable_callback) { + if (peo->writable_callback) { + swoole_event_defer(sw_callable_free, peo->writable_callback); } - sw_zend_fci_cache_persist(&fci_cache_write); - reactor_fd->fci_cache_write = fci_cache_write; + peo->writable_callback = writable_callback; } - - if ((events & SW_EVENT_READ) && reactor_fd->fci_cache_read.function_handler == nullptr) { + if ((events & SW_EVENT_READ) && peo->readable_callback == nullptr) { php_swoole_fatal_error( - E_WARNING, "%s: unable to find read callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } - if ((events & SW_EVENT_WRITE) && reactor_fd->fci_cache_write.function_handler == nullptr) { + if ((events & SW_EVENT_WRITE) && peo->writable_callback == nullptr) { php_swoole_fatal_error( - E_WARNING, "%s: unable to find write callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } if (swoole_event_set(socket, events) < 0) { @@ -616,9 +579,9 @@ static PHP_FUNCTION(swoole_event_del) { RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { @@ -639,67 +602,57 @@ static PHP_FUNCTION(swoole_event_del) { } static PHP_FUNCTION(swoole_event_defer) { - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); + zval *zfn; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, *fci_cache) - ZEND_PARSE_PARAMETERS_END_EX(efree(fci_cache); RETURN_FALSE); + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_swoole_check_reactor(); - - sw_zend_fci_cache_persist(fci_cache); - swoole_event_defer(event_defer_callback, fci_cache); + auto fn = sw_callable_create(zfn); + swoole_event_defer(event_defer_callback, fn); RETURN_TRUE; } static PHP_FUNCTION(swoole_event_cycle) { - if (!sw_reactor()) { - php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call %s", ZSTR_VAL(swoole_event_ce->name)); - RETURN_FALSE; - } - - zend_fcall_info _fci = empty_fcall_info; - zend_fcall_info_cache _fci_cache = empty_fcall_info_cache; + zval *zcallback; zend_bool before = 0; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_FUNC_EX(_fci, _fci_cache, 1, 0) + Z_PARAM_ZVAL(zcallback) Z_PARAM_OPTIONAL Z_PARAM_BOOL(before) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (_fci.size == 0) { + event_check_reactor(); + + if (ZVAL_IS_NULL(zcallback)) { if (sw_reactor()->idle_task.callback == nullptr) { RETURN_FALSE; } else { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->idle_task.data); sw_reactor()->idle_task.callback = nullptr; sw_reactor()->idle_task.data = nullptr; RETURN_TRUE; } } - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - - *fci_cache = _fci_cache; - sw_zend_fci_cache_persist(fci_cache); - + auto callback = sw_callable_create(zcallback); if (!before) { if (sw_reactor()->idle_task.data != nullptr) { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->idle_task.data); } sw_reactor()->idle_task.callback = event_end_callback; - sw_reactor()->idle_task.data = fci_cache; + sw_reactor()->idle_task.data = callback; } else { if (sw_reactor()->future_task.data != nullptr) { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->future_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->future_task.data); } sw_reactor()->future_task.callback = event_end_callback; - sw_reactor()->future_task.data = fci_cache; + sw_reactor()->future_task.data = callback; // Registration onBegin callback function sw_reactor()->activate_future_task(); } @@ -721,14 +674,13 @@ static PHP_FUNCTION(swoole_event_wait) { static PHP_FUNCTION(swoole_event_rshutdown) { /* prevent the program from jumping out of the rshutdown */ zend_try { - if (!sw_reactor()) { - return; - } // when throw Exception, do not show the info - if (!sw_reactor()->bailout) { - php_swoole_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated"); + if (!php_swoole_is_fatal_error() && sw_reactor()) { + if (!sw_reactor()->bailout) { + php_swoole_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated"); + } + php_swoole_event_wait(); } - php_swoole_event_wait(); } zend_end_try(); } @@ -738,17 +690,9 @@ static PHP_FUNCTION(swoole_event_dispatch) { RETURN_FALSE; } sw_reactor()->once = true; - -#ifdef HAVE_SIGNALFD - if (sw_reactor()->check_signalfd) { - swoole_signalfd_setup(sw_reactor()); - } -#endif - if (sw_reactor()->wait(nullptr) < 0) { php_swoole_sys_error(E_ERROR, "reactor wait failed"); } - sw_reactor()->once = false; RETURN_TRUE; } diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 2949b9eb7f4..f59d8050a96 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,7 +22,9 @@ #include "swoole_socket.h" #include "swoole_util.h" -#ifdef SW_USE_HTTP2 +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http2_client_coro_arginfo.h" +END_EXTERN_C() #include "swoole_http2.h" @@ -30,6 +32,7 @@ using namespace swoole; using swoole::coroutine::Socket; +using swoole::http2::get_default_setting; namespace Http2 = swoole::http2; @@ -72,22 +75,24 @@ class Client { bool open_ssl; double timeout = network::Socket::default_read_timeout; - Socket *client = nullptr; - - nghttp2_hd_inflater *inflater = nullptr; - nghttp2_hd_deflater *deflater = nullptr; - uint32_t stream_id = 0; // the next send stream id uint32_t last_stream_id = 0; // the last received stream id Http2::Settings local_settings = {}; Http2::Settings remote_settings = {}; + // flow control + uint32_t remote_window_size = 0; + uint32_t local_window_size = 0; + std::unordered_map streams; + std::queue send_queue; /* safety zval */ zval _zobject; zval *zobject; + Socket *socket_ = nullptr; + zval zsocket; Client(const char *_host, size_t _host_len, int _port, bool _ssl, zval *__zobject) { host = std::string(_host, _host_len); @@ -96,6 +101,7 @@ class Client { _zobject = *__zobject; zobject = &_zobject; Http2::init_settings(&local_settings); + local_window_size = local_settings.init_window_size; } inline Stream *get_stream(uint32_t stream_id) { @@ -110,12 +116,11 @@ class Client { ssize_t build_header(zval *zobject, zval *zrequest, char *buffer); inline void update_error_properties(int code, const char *msg) { - zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), code); - zend_update_property_string(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), msg); + php_swoole_socket_set_error_properties(zobject, code, msg); } inline void io_error() { - update_error_properties(client->errCode, client->errMsg); + update_error_properties(socket_->errCode, socket_->errMsg); } inline void nghttp2_error(int code, const char *msg) { @@ -123,27 +128,21 @@ class Client { } inline bool is_available() { - if (sw_unlikely(!client || !client->is_connected())) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_http2_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("errMsg"), - "client is not connected to server"); + if (sw_unlikely(!socket_ || !socket_->is_connected())) { + php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION); return false; } return true; } inline void apply_setting(zval *zset) { - if (client && ZVAL_IS_ARRAY(zset)) { - php_swoole_client_set(client, zset); + if (socket_ && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(socket_, zset); } } inline bool recv_packet(double timeout) { - if (sw_unlikely(client->recv_packet(timeout) <= 0)) { + if (sw_unlikely(socket_->recv_packet(timeout) <= 0)) { io_error(); return false; } @@ -174,20 +173,54 @@ class Client { bool send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len); ReturnCode parse_frame(zval *return_value, bool pipeline_read = false); bool close(); + void socket_dtor(); ~Client() { close(); } private: + nghttp2_hd_inflater *inflater = nullptr; + nghttp2_hd_deflater *deflater = nullptr; + bool send_setting(); int parse_header(Stream *stream, int flags, char *in, size_t inlen); + void clean_send_queue() { + while (send_queue.size() > 0) { + zend_string *frame = send_queue.front(); + send_queue.pop(); + zend_string_release(frame); + } + } + inline bool send(const char *buf, size_t len) { - if (sw_unlikely(client->send_all(buf, len) != (ssize_t) len)) { + if (socket_->has_bound(SW_EVENT_WRITE)) { + if (send_queue.size() > remote_settings.max_concurrent_streams) { + socket_->errCode = SW_ERROR_QUEUE_FULL; + socket_->errMsg = "the send queue is full, try again later"; + io_error(); + return false; + } + send_queue.push(zend_string_init(buf, len, 0)); + return true; + } + if (sw_unlikely(socket_->send_all(buf, len) != (ssize_t) len)) { io_error(); return false; } + while (send_queue.size() > 0) { + zend_string *frame = send_queue.front(); + if (sw_unlikely(socket_->send_all(frame->val, frame->len) != (ssize_t) frame->len)) { + io_error(); + zend_throw_exception(swoole_http2_client_coro_exception_ce, + "failed to send control frame", + SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED); + return false; + } + send_queue.pop(); + zend_string_release(frame); + } return true; } }; @@ -201,33 +234,28 @@ using swoole::coroutine::http2::Stream; using swoole::http2::HeaderSet; struct Http2ClientObject { - Client *h2c; + Client *client; zend_object std; }; -static sw_inline Http2ClientObject *php_swoole_http2_client_coro_fetch_object(zend_object *obj) { +static sw_inline Http2ClientObject *http2_client_coro_fetch_object(zend_object *obj) { return (Http2ClientObject *) ((char *) obj - swoole_http2_client_coro_handlers.offset); } -static sw_inline Client *php_swoole_get_h2c(zval *zobject) { - return php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c; +static sw_inline Client *http2_client_coro_get_client(zval *zobject) { + return http2_client_coro_fetch_object(Z_OBJ_P(zobject))->client; } -static sw_inline void php_swoole_set_h2c(zval *zobject, Client *h2c) { - php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c = h2c; -} - -static void php_swoole_http2_client_coro_free_object(zend_object *object) { - Http2ClientObject *request = php_swoole_http2_client_coro_fetch_object(object); - Client *h2c = request->h2c; - - if (h2c) { - delete h2c; +static void http2_client_coro_free_object(zend_object *object) { + Http2ClientObject *h2o = http2_client_coro_fetch_object(object); + if (h2o->client) { + delete h2o->client; + h2o->client = nullptr; } - zend_object_std_dtor(&request->std); + zend_object_std_dtor(&h2o->std); } -static zend_object *php_swoole_http2_client_coro_create_object(zend_class_entry *ce) { +static zend_object *http2_client_coro_create_object(zend_class_entry *ce) { Http2ClientObject *request = (Http2ClientObject *) zend_object_alloc(sizeof(Http2ClientObject), ce); zend_object_std_init(&request->std, ce); object_properties_init(&request->std, ce); @@ -252,108 +280,66 @@ static PHP_METHOD(swoole_http2_client_coro, close); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, open_ssl) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_stats, 0, 0, 0) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_isStreamExist, 0, 0, 1) - ZEND_ARG_INFO(0, stream_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, request) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_write, 0, 0, 2) - ZEND_ARG_INFO(0, stream_id) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, end_stream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_goaway, 0, 0, 0) - ZEND_ARG_INFO(0, error_code) - ZEND_ARG_INFO(0, debug_data) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_http2_client_methods[] = { - PHP_ME(swoole_http2_client_coro, __construct, arginfo_swoole_http2_client_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, set, arginfo_swoole_http2_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, connect, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, stats, arginfo_swoole_http2_client_coro_stats, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_swoole_http2_client_coro_isStreamExist, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, send, arginfo_swoole_http2_client_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, write, arginfo_swoole_http2_client_coro_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, recv, arginfo_swoole_http2_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, read, arginfo_swoole_http2_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, goaway, arginfo_swoole_http2_client_coro_goaway, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, ping, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, __construct, arginfo_class_Swoole_Coroutine_Http2_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Http2_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, set, arginfo_class_Swoole_Coroutine_Http2_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, connect, arginfo_class_Swoole_Coroutine_Http2_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, stats, arginfo_class_Swoole_Coroutine_Http2_Client_stats, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_class_Swoole_Coroutine_Http2_Client_isStreamExist, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, send, arginfo_class_Swoole_Coroutine_Http2_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, write, arginfo_class_Swoole_Coroutine_Http2_Client_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, recv, arginfo_class_Swoole_Coroutine_Http2_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, read, arginfo_class_Swoole_Coroutine_Http2_Client_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, goaway, arginfo_class_Swoole_Coroutine_Http2_Client_goaway, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, ping, arginfo_class_Swoole_Coroutine_Http2_Client_ping, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, close, arginfo_class_Swoole_Coroutine_Http2_Client_close, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_http2_client_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_http2_client_coro, - "Swoole\\Coroutine\\Http2\\Client", - nullptr, - "Co\\Http2\\Client", - swoole_http2_client_methods); + SW_INIT_CLASS_ENTRY( + swoole_http2_client_coro, "Swoole\\Coroutine\\Http2\\Client", "Co\\Http2\\Client", swoole_http2_client_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_client_coro); SW_SET_CLASS_CLONEABLE(swoole_http2_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_client_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_http2_client_coro, - php_swoole_http2_client_coro_create_object, - php_swoole_http2_client_coro_free_object, + http2_client_coro_create_object, + http2_client_coro_free_object, Http2ClientObject, std); SW_INIT_CLASS_ENTRY_EX(swoole_http2_client_coro_exception, "Swoole\\Coroutine\\Http2\\Client\\Exception", - nullptr, "Co\\Http2\\Client\\Exception", nullptr, swoole_exception); - SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", "swoole_http2_request", nullptr, nullptr); + SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", nullptr, nullptr); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_request); SW_SET_CLASS_CLONEABLE(swoole_http2_request, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_request, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_request); - SW_INIT_CLASS_ENTRY(swoole_http2_response, "Swoole\\Http2\\Response", "swoole_http2_response", nullptr, nullptr); + SW_INIT_CLASS_ENTRY(swoole_http2_response, "Swoole\\Http2\\Response", nullptr, nullptr); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_response); SW_SET_CLASS_CLONEABLE(swoole_http2_response, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_response, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_response); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errMsg"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_http2_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("serverLastStreamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("path"), "/", ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("method"), "GET", ZEND_ACC_PUBLIC); @@ -361,6 +347,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("usePipelineRead"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); @@ -395,40 +382,65 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CONNECT_ERROR", SW_HTTP2_ERROR_CONNECT_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_ENHANCE_YOUR_CALM", SW_HTTP2_ERROR_ENHANCE_YOUR_CALM); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INADEQUATE_SECURITY", SW_HTTP2_ERROR_INADEQUATE_SECURITY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_HTTP_1_1_REQUIRED", SW_HTTP2_ERROR_HTTP_1_1_REQUIRED); +} + +void Client::socket_dtor() { + socket_ = nullptr; + clean_send_queue(); + auto i = streams.begin(); + while (i != streams.end()) { + destroy_stream(i->second); + streams.erase(i++); + } + if (inflater) { + nghttp2_hd_inflate_del(inflater); + inflater = nullptr; + } + if (deflater) { + nghttp2_hd_deflate_del(deflater); + deflater = nullptr; + } + zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + zend_update_property_null(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + zval_ptr_dtor(&zsocket); + ZVAL_NULL(&zsocket); } bool Client::connect() { - if (sw_unlikely(client != nullptr)) { + if (sw_unlikely(socket_ != nullptr)) { + update_error_properties(EISCONN, strerror(EISCONN)); return false; } - client = new Socket(network::Socket::convert_to_type(host)); - if (UNEXPECTED(client->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); - zend_update_property_string( - swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete client; - client = nullptr; + auto object = php_swoole_create_socket(network::Socket::convert_to_type(host)); + if (UNEXPECTED(!object)) { + php_swoole_socket_set_error_properties(zobject, errno, strerror(errno)); return false; } - client->set_zero_copy(true); + + ZVAL_OBJ(&zsocket, object); + socket_ = php_swoole_get_socket(&zsocket); + socket_->set_dtor([this](Socket *_socket) { socket_dtor(); }); + socket_->set_zero_copy(true); #ifdef SW_USE_OPENSSL - if (open_ssl) { - client->enable_ssl_encrypt(); + if (open_ssl && !socket_->enable_ssl_encrypt()) { + io_error(); + close(); + return false; } #endif - client->http2 = 1; - client->open_length_check = 1; - client->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; - client->protocol.package_length_offset = 0; - client->protocol.package_body_offset = 0; - client->protocol.get_package_length = Http2::get_frame_length; + socket_->http2 = 1; + socket_->open_length_check = 1; + socket_->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; + socket_->protocol.package_length_offset = 0; + socket_->protocol.package_body_offset = 0; + socket_->protocol.get_package_length = Http2::get_frame_length; apply_setting( sw_zend_read_property_ex(swoole_http2_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0)); - if (!client->connect(host, port)) { + if (!socket_->connect(host, port)) { io_error(); close(); return false; @@ -437,6 +449,7 @@ bool Client::connect() { stream_id = 1; // [init]: we must set default value, server is not always send all the settings Http2::init_settings(&remote_settings); + remote_window_size = remote_settings.init_window_size; int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem()); if (ret != 0) { @@ -461,41 +474,32 @@ bool Client::connect() { return false; } + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &zsocket); zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } bool Client::close() { - Socket *_client = client; - if (!_client) { + /* + * The socket_ pointer MUST be staged, + * when client close the member variable may be set to nullptr in socket dtor + */ + Socket *_socket = socket_; + if (_socket == nullptr) { + update_error_properties(EBADF, strerror(EBADF)); return false; } - zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!_client->has_bound()) { - auto i = streams.begin(); - while (i != streams.end()) { - destroy_stream(i->second); - streams.erase(i++); - } - if (inflater) { - nghttp2_hd_inflate_del(inflater); - inflater = nullptr; - } - if (deflater) { - nghttp2_hd_deflate_del(deflater); - deflater = nullptr; - } - client = nullptr; - } - if (_client->close()) { - delete _client; + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + if (!_socket->close()) { + update_error_properties(_socket->errCode, _socket->errMsg); + return false; } return true; } ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { - char *buf = client->get_read_buffer()->str; + char *buf = socket_->get_read_buffer()->str; uint8_t type = buf[3]; uint8_t flags = buf[4]; uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff; @@ -514,14 +518,14 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swoole_http2_frame_trace_log(recv, "ACK"); + swoole_http2_frame_trace_log("ACK"); return SW_CONTINUE; } while (length > 0) { id = ntohs(*(uint16_t *) (buf)); value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); - swoole_http2_frame_trace_log(recv, "id=%d, value=%d", id, value); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); switch (id) { case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: if (value != remote_settings.header_table_size) { @@ -539,7 +543,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); break; case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - remote_settings.window_size = value; + remote_window_size = remote_settings.init_window_size = value; swoole_trace_log(SW_TRACE_HTTP2, "setting: init_send_window=%u", value); break; case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: @@ -549,14 +553,13 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: if (value != remote_settings.max_header_list_size) { remote_settings.max_header_list_size = value; - /* +#if 0 int ret = nghttp2_hd_inflate_change_table_size(inflater, value); - if (ret != 0) - { + if (ret != 0) { nghttp2_error(ret, "nghttp2_hd_inflate_change_table_size() failed"); return SW_ERROR; } - */ +#endif } swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); break; @@ -577,9 +580,10 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_WINDOW_UPDATE: { value = ntohl(*(uint32_t *) buf); - swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value); + swoole_trace_log( + SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, value); if (stream_id == 0) { - remote_settings.window_size += value; + remote_window_size += value; } else { Stream *stream = get_stream(stream_id); if (stream) { @@ -589,7 +593,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } case SW_HTTP2_TYPE_PING: { - swoole_http2_frame_trace_log(recv, "ping"); + swoole_http2_frame_trace_log("ping"); if (!(flags & SW_HTTP2_FLAG_ACK)) { Http2::set_frame_header( frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id); @@ -606,8 +610,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swoole_http2_frame_trace_log(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", + swoole_http2_frame_trace_log("last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", server_last_stream_id, value, (int) (length - SW_HTTP2_GOAWAY_SIZE), @@ -624,7 +627,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_RST_STREAM: { value = ntohl(*(uint32_t *) (buf)); - swoole_http2_frame_trace_log(recv, "error_code=%d", value); + swoole_http2_frame_trace_log("error_code=%d", value); // delete and free quietly delete_stream(stream_id); @@ -637,7 +640,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { case SW_HTTP2_TYPE_PUSH_PROMISE: { #ifdef SW_DEBUG uint32_t promise_stream_id = ntohl(*(uint32_t *) (buf)) & 0x7fffffff; - swoole_http2_frame_trace_log(recv, "promise_stream_id=%d", promise_stream_id); + swoole_http2_frame_trace_log("promise_stream_id=%d", promise_stream_id); #endif // auto promise_stream = create_stream(promise_stream_id, false); // RETVAL_ZVAL(promise_stream->response_object, 0, 0); @@ -645,7 +648,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } default: { - swoole_http2_frame_trace_log(recv, ""); + swoole_http2_frame_trace_log(""); } } @@ -680,19 +683,19 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { // now we control the connection flow only (not stream) // our window size is unlimited, so we don't worry about subtraction overflow - local_settings.window_size -= length; + local_window_size -= length; stream->local_window_size -= length; - if (local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - if (!send_window_update(0, SW_HTTP2_MAX_WINDOW_SIZE - local_settings.window_size)) { + if (local_window_size < (local_settings.init_window_size / 4)) { + if (!send_window_update(0, local_settings.init_window_size - local_window_size)) { return SW_ERROR; } - local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE; + local_window_size = local_settings.init_window_size; } - if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - if (!send_window_update(stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size)) { + if (stream->local_window_size < (local_settings.init_window_size / 4)) { + if (!send_window_update(stream_id, local_settings.init_window_size - stream->local_window_size)) { return SW_ERROR; } - stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE; + stream->local_window_size = local_settings.init_window_size; } } } @@ -746,10 +749,13 @@ int php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int stream->total_in = 0; stream->total_out = 0; -#if 0 - printf(SW_START_LINE"\nstatus=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld\n", status, - stream->avail_in, stream->avail_out, stream->total_in, stream->total_out); -#endif + swoole_trace_log(SW_TRACE_ZLIB, + SW_START_LINE "\nstatus=%d\tavail_in=%u,\tavail_out=%u,\ttotal_in=%lu,\ttotal_out=%lu\n", + status, + stream->avail_in, + stream->avail_out, + stream->total_in, + stream->total_out); buffer->clear(); @@ -759,10 +765,15 @@ int php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int status = inflate(stream, Z_SYNC_FLUSH); -#if 0 - printf("status=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld,\tlength=%ld\n", status, - stream->avail_in, stream->avail_out, stream->total_in, stream->total_out, buffer->length); -#endif + swoole_trace_log(SW_TRACE_ZLIB, + "status=%d\tavail_in=%d,\tavail_out=%d,\ttotal_in=%lu,\ttotal_out=%lu,\tlength=%lu\n", + status, + stream->avail_in, + stream->avail_out, + stream->total_in, + stream->total_out, + buffer->length); + if (status >= 0) { buffer->length = stream->total_out; } @@ -803,19 +814,19 @@ static PHP_METHOD(swoole_http2_client_coro, __construct) { RETURN_FALSE; } - Client *h2c = new Client(host, host_len, port, ssl, ZEND_THIS); + Client *client = new Client(host, host_len, port, ssl, ZEND_THIS); if (ssl) { #ifndef SW_USE_OPENSSL zend_throw_exception_ex( swoole_http2_client_coro_exception_ce, EPROTONOSUPPORT, "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - delete h2c; + delete client; RETURN_FALSE; #endif } - php_swoole_set_h2c(ZEND_THIS, h2c); + http2_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS))->client = client; zend_update_property_stringl( swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("host"), host, host_len); @@ -824,7 +835,7 @@ static PHP_METHOD(swoole_http2_client_coro, __construct) { } static PHP_METHOD(swoole_http2_client_coro, set) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zval *zset; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -840,57 +851,37 @@ static PHP_METHOD(swoole_http2_client_coro, set) { RETURN_TRUE; } +/** + * called in read channel + */ bool Client::send_window_update(int stream_id, uint32_t size) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE]; - swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); + swoole_http2_send_trace_log("[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size); Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); } +/** + * called on connect + */ bool Client::send_setting() { - Http2::Settings *settings = &local_settings; - uint16_t id = 0; - uint32_t value = 0; - - char frame[SW_HTTP2_FRAME_HEADER_SIZE + 18]; - memset(frame, 0, sizeof(frame)); - Http2::set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 18, 0, 0); - - char *p = frame + SW_HTTP2_FRAME_HEADER_SIZE; - /** - * HEADER_TABLE_SIZE - */ - id = htons(SW_HTTP2_SETTING_HEADER_TABLE_SIZE); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->header_table_size); - memcpy(p, &value, sizeof(value)); - p += 4; - /** - * MAX_CONCURRENT_STREAMS - */ - id = htons(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->max_concurrent_streams); - memcpy(p, &value, sizeof(value)); - p += 4; - /** - * INIT_WINDOW_SIZE - */ - id = htons(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->window_size); - memcpy(p, &value, sizeof(value)); - p += 4; - - swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "]\t[length=%d]", Http2::get_type(SW_HTTP2_TYPE_SETTINGS), 18); - return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + 18); + char frame[SW_HTTP2_SETTING_FRAME_SIZE]; + size_t n = Http2::pack_setting_frame(frame, local_settings, false); + swoole_http2_send_trace_log("[" SW_ECHO_MAGENTA + "] ", + Http2::get_type(SW_HTTP2_TYPE_SETTINGS), + local_settings.header_table_size, + local_settings.enable_push, + local_settings.max_concurrent_streams, + local_settings.init_window_size, + local_settings.max_frame_size, + local_settings.max_header_list_size); + return send(frame, n); } -void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); +void php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { zval *zresponse = &stream->zresponse; @@ -945,7 +936,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } else { #ifdef SW_HAVE_ZLIB if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-encoding") && - SW_STRCASECT((char *) nv.value, nv.valuelen, "gzip")) { + SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "gzip")) { /** * init zlib stream */ @@ -964,7 +955,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } else #endif if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "set-cookie")) { - http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers); + php_swoole_http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers); } add_assoc_stringl_ex(zheaders, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen); } @@ -981,7 +972,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { - Client *h2c = php_swoole_get_h2c(zobject); + Client *h2c = http2_client_coro_get_client(zobject); zval *zmethod = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_METHOD), 0); zval *zpath = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH), 0); zval *zheaders = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADERS), 0); @@ -1046,7 +1037,7 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { zend_string *key; zval *zvalue; char *encoded_value; - int encoded_value_len; + size_t encoded_value_len; String *buffer = sw_tg_buffer(); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue) { @@ -1068,11 +1059,14 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { } size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len()); - // if (buflen > h2c->remote_settings.max_header_list_size) { - // php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", - // h2c->remote_settings.max_header_list_size); - // return -1; - // } +#if 0 + if (buflen > h2c->remote_settings.max_header_list_size) { + php_swoole_error(E_WARNING, + "header cannot bigger than remote max_header_list_size %u", + client->remote_settings.max_header_list_size); + return -1; + } +#endif ssize_t rv = nghttp2_hd_deflate_hd(h2c->deflater, (uchar *) buffer, buflen, headers.get(), headers.len()); if (rv < 0) { h2c->nghttp2_error(rv, "nghttp2_hd_deflate_hd() failed"); @@ -1101,8 +1095,8 @@ Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { // init stream->stream_id = stream_id; stream->flags = flags; - stream->remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - stream->local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; + stream->remote_window_size = remote_settings.init_window_size; + stream->local_window_size = local_settings.init_window_size; streams.emplace(stream_id, stream); // create response object object_init_ex(&stream->zresponse, swoole_http2_response_ce); @@ -1112,9 +1106,13 @@ Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { return stream; } +/** + * called in write channel + */ bool Client::send_ping_frame() { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + swoole_http2_send_trace_log("[" SW_ECHO_CYAN "]", "PING"); return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); } @@ -1143,6 +1141,9 @@ bool Client::send_data(uint32_t stream_id, const char *p, size_t len, int flag) return true; } +/** + * called in write channel + */ uint32_t Client::send_request(zval *zrequest) { zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_request_ce, zrequest, ZEND_STRL("headers"), 0); @@ -1186,11 +1187,8 @@ uint32_t Client::send_request(zval *zrequest) { Http2::set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id); - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ", STREAM#%d] length=%zd", - Http2::get_type(SW_HTTP2_TYPE_HEADERS), - stream->stream_id, - bytes); + swoole_http2_send_trace_log( + "[" SW_ECHO_GREEN ", STREAM#%d] length=%zd", Http2::get_type(SW_HTTP2_TYPE_HEADERS), stream->stream_id, bytes); if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE + bytes)) { return 0; } @@ -1217,11 +1215,10 @@ uint32_t Client::send_request(zval *zrequest) { len = str_zpost_data.len(); } - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", - Http2::get_type(SW_HTTP2_TYPE_DATA), - stream->stream_id, - len); + swoole_http2_send_trace_log("[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", + Http2::get_type(SW_HTTP2_TYPE_DATA), + stream->stream_id, + len); if (!send_data(stream->stream_id, p, len, flag)) { return 0; @@ -1237,6 +1234,9 @@ uint32_t Client::send_request(zval *zrequest) { return stream->stream_id; } +/** + * called in write channel + */ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { char buffer[SW_HTTP2_FRAME_HEADER_SIZE]; Stream *stream = get_stream(stream_id); @@ -1290,6 +1290,9 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { return true; } +/** + * called in write channel + */ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len) { size_t length = SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE + debug_data_len; char *frame = (char *) ecalloc(1, length); @@ -1300,18 +1303,17 @@ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, siz if (debug_data_len > 0) { memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len); } - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN "] Send: last-sid=%u, error-code=%ld", - Http2::get_type(SW_HTTP2_TYPE_GOAWAY), - last_stream_id, - error_code); + swoole_http2_send_trace_log("[" SW_ECHO_RED "] last-sid=%u, error-code=" ZEND_LONG_FMT, + Http2::get_type(SW_HTTP2_TYPE_GOAWAY), + last_stream_id, + error_code); ret = send(frame, length); efree(frame); return ret; } static PHP_METHOD(swoole_http2_client_coro, send) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1323,6 +1325,8 @@ static PHP_METHOD(swoole_http2_client_coro, send) { Z_PARAM_OBJECT_OF_CLASS(zrequest, swoole_http2_request_ce) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); + uint32_t stream_id = h2c->send_request(zrequest); if (stream_id == 0) { RETURN_FALSE; @@ -1331,9 +1335,8 @@ static PHP_METHOD(swoole_http2_client_coro, send) { } } -static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); - +static void http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1341,6 +1344,8 @@ static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + while (true) { if (!h2c->is_available()) { RETURN_FALSE; @@ -1360,47 +1365,51 @@ static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool } static PHP_METHOD(swoole_http2_client_coro, recv) { - php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_http2_client_coro, __destruct) {} static PHP_METHOD(swoole_http2_client_coro, close) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); RETURN_BOOL(h2c->close()); } static PHP_METHOD(swoole_http2_client_coro, connect) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); RETURN_BOOL(h2c->connect()); } -static sw_inline void http2_settings_to_array(Http2::Settings *settings, zval *zarray) { +static sw_inline void http2_client_settings_to_array(Http2::Settings *settings, zval *zarray) { array_init(zarray); add_assoc_long_ex(zarray, ZEND_STRL("header_table_size"), settings->header_table_size); - add_assoc_long_ex(zarray, ZEND_STRL("window_size"), settings->window_size); + add_assoc_long_ex(zarray, ZEND_STRL("init_window_size"), settings->init_window_size); add_assoc_long_ex(zarray, ZEND_STRL("max_concurrent_streams"), settings->max_concurrent_streams); add_assoc_long_ex(zarray, ZEND_STRL("max_frame_size"), settings->max_frame_size); add_assoc_long_ex(zarray, ZEND_STRL("max_header_list_size"), settings->max_header_list_size); } static PHP_METHOD(swoole_http2_client_coro, stats) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zval _zarray, *zarray = &_zarray; String key = {}; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &key.str, &key.length) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(key.str, key.length); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (key.length > 0) { if (SW_STREQ(key.str, key.length, "current_stream_id")) { RETURN_LONG(h2c->stream_id); } else if (SW_STREQ(key.str, key.length, "last_stream_id")) { RETURN_LONG(h2c->last_stream_id); } else if (SW_STREQ(key.str, key.length, "local_settings")) { - http2_settings_to_array(&h2c->local_settings, zarray); + http2_client_settings_to_array(&h2c->local_settings, zarray); RETURN_ZVAL(zarray, 0, 0); } else if (SW_STREQ(key.str, key.length, "remote_settings")) { - http2_settings_to_array(&h2c->remote_settings, zarray); + http2_client_settings_to_array(&h2c->remote_settings, zarray); RETURN_ZVAL(zarray, 0, 0); } else if (SW_STREQ(key.str, key.length, "active_stream_num")) { RETURN_LONG(h2c->streams.size()); @@ -1409,9 +1418,9 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { array_init(return_value); add_assoc_long_ex(return_value, ZEND_STRL("current_stream_id"), h2c->stream_id); add_assoc_long_ex(return_value, ZEND_STRL("last_stream_id"), h2c->last_stream_id); - http2_settings_to_array(&h2c->local_settings, zarray); + http2_client_settings_to_array(&h2c->local_settings, zarray); add_assoc_zval_ex(return_value, ZEND_STRL("local_settings"), zarray); - http2_settings_to_array(&h2c->remote_settings, zarray); + http2_client_settings_to_array(&h2c->remote_settings, zarray); add_assoc_zval_ex(return_value, ZEND_STRL("remote_settings"), zarray); add_assoc_long_ex(return_value, ZEND_STRL("active_stream_num"), h2c->streams.size()); } @@ -1419,15 +1428,17 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { zend_long stream_id = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &stream_id) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(stream_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (stream_id < 0) { RETURN_FALSE; } - Client *h2c = php_swoole_get_h2c(ZEND_THIS); - if (!h2c->client) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + if (!h2c->socket_) { RETURN_FALSE; } else if (stream_id == 0) { RETURN_TRUE; @@ -1437,7 +1448,7 @@ static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { } static PHP_METHOD(swoole_http2_client_coro, write) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1446,18 +1457,25 @@ static PHP_METHOD(swoole_http2_client_coro, write) { zend_long stream_id; zval *data; zend_bool end = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b", &stream_id, &data, &end) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_LONG(stream_id); + Z_PARAM_ZVAL(data); + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(end); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); + RETURN_BOOL(h2c->write_data(stream_id, data, end)); } static PHP_METHOD(swoole_http2_client_coro, read) { - php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_http2_client_coro, ping) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1476,7 +1494,7 @@ static PHP_METHOD(swoole_http2_client_coro, ping) { * +---------------------------------------------------------------+ */ static PHP_METHOD(swoole_http2_client_coro, goaway) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zend_long error_code = SW_HTTP2_ERROR_NO_ERROR; char *debug_data = nullptr; size_t debug_data_len = 0; @@ -1485,11 +1503,12 @@ static PHP_METHOD(swoole_http2_client_coro, goaway) { RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(error_code); + Z_PARAM_STRING(debug_data, debug_data_len); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len)); } - -#endif diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index c84ef5e0f38..ddfe9103e71 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -10,13 +10,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_http_server.h" -#ifdef SW_USE_HTTP2 +#include #include "swoole_static_handler.h" @@ -25,6 +25,7 @@ using namespace swoole; using std::string; using swoole::coroutine::System; +using swoole::http2::get_default_setting; using swoole::http_server::StaticHandler; namespace Http2 = swoole::http2; @@ -33,10 +34,11 @@ using HttpContext = swoole::http::Context; using Http2Stream = Http2::Stream; using Http2Session = Http2::Session; -static std::unordered_map http2_sessions; -extern String *swoole_http_buffer; +static SW_THREAD_LOCAL std::unordered_map http2_sessions; -static bool swoole_http2_server_respond(HttpContext *ctx, String *body); +static bool http2_server_respond(HttpContext *ctx, const String *body); +static bool http2_server_write(HttpContext *ctx, const String *chunk); +static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler); Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx = swoole_http_context_new(client->fd); @@ -45,8 +47,8 @@ Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx->stream = this; ctx->keepalive = true; id = _id; - local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; + local_window_size = client->local_settings.init_window_size; + remote_window_size = client->remote_settings.init_window_size; } Http2Stream::~Stream() { @@ -69,6 +71,8 @@ Http2Session::Session(SessionId _fd) { Http2::init_settings(&local_settings); // [init]: we must set default value, peer is not always send all the settings Http2::init_settings(&remote_settings); + local_window_size = local_settings.init_window_size; + remote_window_size = remote_settings.init_window_size; last_stream_id = 0; shutting_down = false; is_coro = false; @@ -100,7 +104,7 @@ static void http2_server_send_window_update(HttpContext *ctx, uint32_t stream_id ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); } -static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { +static ssize_t http2_server_build_trailer(HttpContext *ctx, uchar *buffer) { zval *ztrailer = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); uint32_t size = php_swoole_array_length_safe(ztrailer); @@ -125,7 +129,7 @@ static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { nghttp2_hd_deflater *deflater = client->deflater; if (!deflater) { - int ret = nghttp2_hd_deflate_new2(&deflater, SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE, php_nghttp2_mem()); + int ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); if (ret != 0) { swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; @@ -134,13 +138,14 @@ static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { } buflen = nghttp2_hd_deflate_bound(deflater, trailer.get(), trailer.len()); - /* - if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) - { - php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", - SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); return -1; +#if 0 + if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) { + php_swoole_error(E_WARNING, + "header cannot bigger than remote max_header_list_size %u", + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + return -1; } - */ +#endif rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, trailer.get(), trailer.len()); if (rv < 0) { swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); @@ -151,7 +156,7 @@ static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { return 0; } -static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { +static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { zval *zserver = ctx->request.zserver; zval *zrequest_uri = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL("request_uri")); if (zrequest_uri && Z_TYPE_P(zrequest_uri) == IS_STRING) { @@ -161,18 +166,36 @@ static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { } if (handler.status_code == SW_HTTP_NOT_FOUND) { - String null_body = {}; - + String body(SW_STRL(SW_HTTP_PAGE_404)); ctx->response.status = SW_HTTP_NOT_FOUND; - swoole_http2_server_respond(ctx, &null_body); + http2_server_respond(ctx, &body); + return true; + } + + /** + * if http_index_files is enabled, need to search the index file first. + * if the index file is found, set filename to index filename. + */ + if (!handler.hit_index_file()) { + return false; + } + + /** + * the index file was not found in the current directory, + * if http_autoindex is enabled, should show the list of files in the current directory. + */ + if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) { + String body(PATH_MAX); + body.length = handler.make_index_page(&body); + http2_server_respond(ctx, &body); return true; } auto date_str = handler.get_date(); auto date_str_last_modified = handler.get_date_last_modified(); - zval *zheader = ctx->request.zserver; - ctx->set_header(ZEND_STRL("Last-Modified"), date_str.c_str(), date_str.length(), 0); + zval *zheader = ctx->request.zheader; + ctx->set_header(ZEND_STRL("Last-Modified"), date_str_last_modified.c_str(), date_str_last_modified.length(), 0); zval *zdate_if_modified_since = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("if-modified-since")); if (zdate_if_modified_since) { @@ -183,34 +206,62 @@ static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { } } - zend::String _filename(handler.get_filename_std_string()); - zval zfilename; - ZVAL_STR(&zfilename, _filename.get()); - zval retval; /* do not care the retval (the connection will be closed if failed) */ - sw_zend_call_method_with_1_params( - ctx->response.zobject, swoole_http_response_ce, nullptr, "sendfile", &retval, &zfilename); + zval *zrange = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("range")); + zval *zif_range = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("if-range")); + handler.parse_range(zrange ? Z_STRVAL_P(zrange) : nullptr, zif_range ? Z_STRVAL_P(zif_range) : nullptr); + ctx->response.status = handler.status_code; + auto tasks = handler.get_tasks(); + if (1 == tasks.size()) { + if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { + std::stringstream content_range; + content_range << "bytes " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" + << handler.get_filesize() << "\r\n"; + auto content_range_str = content_range.str(); + ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); + } else { + ctx->set_header(ZEND_STRL("Accept-Ranges"), SW_STRL("bytes"), 0); + } + } + + ctx->onAfterResponse = nullptr; + ctx->onBeforeRequest = nullptr; - return true; + // request_method + zval *zrequest_method = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL("request_method")); + if (zrequest_method && Z_TYPE_P(zrequest_method) == IS_STRING && + SW_STRCASEEQ(Z_STRVAL_P(zrequest_method), Z_STRLEN_P(zrequest_method), "HEAD")) { + String empty_body; + http2_server_respond(ctx, &empty_body); + return true; + } else { + return http2_server_send_range_file(ctx, &handler); + } } return false; } -static void swoole_http2_onRequest(Http2Session *client, Http2Stream *stream) { +static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { HttpContext *ctx = stream->ctx; zval *zserver = ctx->request.zserver; Server *serv = (Server *) ctx->private_data; + zval args[2]; + Connection *serv_sock = nullptr; + zend::Callable *cb = nullptr; + int server_fd = 0; Connection *conn = serv->get_connection_by_session_id(ctx->fd); - int server_fd = conn->server_fd; - Connection *serv_sock = serv->get_connection(server_fd); + if (!conn) { + goto _destroy; + } - ctx->request.version = SW_HTTP_OK; + server_fd = conn->server_fd; + serv_sock = serv->get_connection(server_fd); - if (serv->enable_static_handler && swoole_http2_is_static_file(serv, ctx)) { - zval_ptr_dtor(ctx->request.zobject); - zval_ptr_dtor(ctx->response.zobject); - return; + ctx->request.version = SW_HTTP_VERSION_2; + + if (serv->enable_static_handler && http2_server_is_static_file(serv, ctx)) { + goto _destroy; } add_assoc_long(zserver, "request_time", time(nullptr)); @@ -223,24 +274,49 @@ static void swoole_http2_onRequest(Http2Session *client, Http2Stream *stream) { add_assoc_long(zserver, "master_time", conn->last_recv_time); add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); - zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest); + ctx->private_data_2 = cb; + + if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { + return; + } + + args[0] = *ctx->request.zobject; + args[1] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR); php_swoole_error(E_WARNING, "%s->onRequest[v2] handler error", ZSTR_VAL(swoole_http_server_ce->name)); } - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[1]); +_destroy: + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); } -static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_length) { +static void http2_server_set_date_header(Http2::HeaderSet *headers) { + static struct { + time_t time; + size_t len; + char buf[64]; + } cache{}; + + time_t now = time(nullptr); + if (now != cache.time) { + char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + cache.len = strlen(date_str); + memcpy(cache.buf, date_str, cache.len); + cache.time = now; + efree(date_str); + } + headers->add(ZEND_STRL("date"), cache.buf, cache.len); +} + +static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const String *body) { zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); zval *zcookie = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); Http2::HeaderSet headers(32 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); - char *date_str = nullptr; char intbuf[2][16]; int ret; @@ -262,8 +338,10 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l zval *zvalue; int type; + zend_string *content_type = nullptr; auto add_header = - [](Http2::HeaderSet &headers, const char *key, size_t l_key, zval *value, uint32_t &header_flags) { + [ctx, &content_type]( + Http2::HeaderSet &headers, const char *key, size_t l_key, zval *value, uint32_t &header_flags) { if (ZVAL_IS_NULL(value)) { return; } @@ -272,14 +350,19 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l if (swoole_http_has_crlf(str_value.val(), str_value.len())) { return; } - if (SW_STREQ(key, l_key, "server")) { + if (SW_STRCASEEQ(key, l_key, "server")) { header_flags |= HTTP_HEADER_SERVER; - } else if (SW_STREQ(key, l_key, "content-length")) { + } else if (SW_STRCASEEQ(key, l_key, "content-length")) { return; // ignore - } else if (SW_STREQ(key, l_key, "date")) { + } else if (SW_STRCASEEQ(key, l_key, "date")) { header_flags |= HTTP_HEADER_DATE; - } else if (SW_STREQ(key, l_key, "content-type")) { + } else if (SW_STRCASEEQ(key, l_key, "content-type")) { header_flags |= HTTP_HEADER_CONTENT_TYPE; +#ifdef SW_HAVE_COMPRESSION + if (ctx->accept_compression && ctx->compression_types) { + content_type = zval_get_string(value); + } +#endif } headers.add(key, l_key, str_value.val(), str_value.len()); }; @@ -300,21 +383,28 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l } SW_HASHTABLE_FOREACH_END(); (void) type; + +#ifdef SW_HAVE_COMPRESSION + if (ctx->accept_compression && ctx->compression_types) { + std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) + : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); + ctx->accept_compression = ctx->compression_types->find(str_content_type) != ctx->compression_types->end(); + if (content_type) { + zend_string_release(content_type); + } + } +#endif } if (!(header_flags & HTTP_HEADER_SERVER)) { headers.add(ZEND_STRL("server"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE)); } if (!(header_flags & HTTP_HEADER_DATE)) { - date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), time(nullptr), 0); - headers.add(ZEND_STRL("date"), date_str, strlen(date_str)); + http2_server_set_date_header(&headers); } if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); } - if (date_str) { - efree(date_str); - } // cookies if (ZVAL_IS_ARRAY(zcookie)) { @@ -328,27 +418,25 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l SW_HASHTABLE_FOREACH_END(); } - // content encoding + if (body) { + size_t content_length = body->length; + // content length #ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - const char *content_encoding = ctx->get_content_encoding(); - headers.add(ZEND_STRL("content-encoding"), (char *) content_encoding, strlen(content_encoding)); - } + if (ctx->compress(body->str, body->length)) { + content_length = ctx->zlib_buffer->length; + // content encoding + const char *content_encoding = ctx->get_content_encoding(); + headers.add(ZEND_STRL("content-encoding"), (char *) content_encoding, strlen(content_encoding)); + } #endif - - // content length -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - body_length = swoole_zlib_buffer->length; + ret = swoole_itoa(intbuf[1], content_length); + headers.add(ZEND_STRL("content-length"), intbuf[1], ret); } -#endif - ret = swoole_itoa(intbuf[1], body_length); - headers.add(ZEND_STRL("content-length"), intbuf[1], ret); Http2Session *client = http2_sessions[ctx->fd]; nghttp2_hd_deflater *deflater = client->deflater; if (!deflater) { - ret = nghttp2_hd_deflate_new2(&deflater, client->local_settings.header_table_size, php_nghttp2_mem()); + ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); if (ret != 0) { swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; @@ -398,14 +486,15 @@ int swoole_http2_server_goaway(HttpContext *ctx, zend_long error_code, const cha return ret; } -bool Http2Stream::send_header(size_t body_length, bool end_stream) { +bool Http2Stream::send_header(const String *body, bool end_stream) { char header_buffer[SW_BUFFER_SIZE_STD]; - ssize_t bytes = http2_build_header(ctx, (uchar *) header_buffer, body_length); + ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body); if (bytes < 0) { return false; } - swoole_http_buffer->clear(); + String *http_buffer = ctx->get_write_buffer(); + http_buffer->clear(); /** +---------------+ @@ -422,17 +511,17 @@ bool Http2Stream::send_header(size_t body_length, bool end_stream) { */ char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; - if (end_stream && body_length == 0) { + if (end_stream && body && body->length == 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); } else { http2::set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, id); } - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(header_buffer, bytes); + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(header_buffer, bytes); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { ctx->send_header_ = 0; return false; } @@ -440,12 +529,19 @@ bool Http2Stream::send_header(size_t body_length, bool end_stream) { return true; } -bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { +bool Http2Stream::send_end_stream_data_frame() { + char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, 0, SW_HTTP2_FLAG_END_STREAM, id); + return ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); +} + +bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; char *p = body->str + offset; size_t l = length == 0 ? body->length : length; int flags = end_stream ? SW_HTTP2_FLAG_END_STREAM : SW_HTTP2_FLAG_NONE; + String *http_buffer = ctx->get_write_buffer(); while (l > 0) { size_t send_n; @@ -460,11 +556,11 @@ bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flags, id); // send twice to reduce memory copy - if (send_n < SwooleG.pagesize) { - swoole_http_buffer->clear(); - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(p, send_n); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + if (send_n < swoole_pagesize()) { + http_buffer->clear(); + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(p, send_n); + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { return false; } } else { @@ -479,7 +575,6 @@ bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size swoole_trace_log( SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, flags=%d, send_n=%lu", "DATA", id, flags, send_n); - l -= send_n; p += send_n; } @@ -490,15 +585,16 @@ bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size bool Http2Stream::send_trailer() { char header_buffer[SW_BUFFER_SIZE_STD] = {}; char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + String *http_buffer = ctx->get_write_buffer(); - swoole_http_buffer->clear(); - ssize_t bytes = http2_build_trailer(ctx, (uchar *) header_buffer); + http_buffer->clear(); + ssize_t bytes = http2_server_build_trailer(ctx, (uchar *) header_buffer); if (bytes > 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(header_buffer, bytes); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(header_buffer, bytes); + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { return false; } } @@ -506,49 +602,21 @@ bool Http2Stream::send_trailer() { return true; } -static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { - Http2Session *client = http2_sessions[ctx->fd]; - Http2Stream *stream = ctx->stream; - -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - if (body->length == 0 || - swoole_http_response_compress(body->str, body->length, ctx->compression_method, ctx->compression_level) != - SW_OK) { - ctx->accept_compression = 0; - } else { - body = swoole_zlib_buffer; - } - } -#endif - - zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); - if (php_swoole_array_length_safe(ztrailer) == 0) { - ztrailer = nullptr; - } - - bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(body->length, end_stream)) { - return false; - } - - // The headers has already been sent, retries are no longer allowed (even if send body failed) - ctx->end_ = 1; - +static bool http2_server_send_data( + HttpContext *ctx, Http2Session *client, Http2Stream *stream, const String *body, bool end_stream) { bool error = false; - // If send_yield is not supported, ignore flow control if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { - if (body->length > client->remote_settings.window_size) { + if (body->length > client->remote_window_size) { swoole_warning("The data sent exceeded remote_window_size"); } if (!stream->send_body(body, end_stream, client->local_settings.max_frame_size)) { error = true; } } else { + off_t offset = body->offset; while (true) { - size_t send_len = body->length - body->offset; + size_t send_len = body->length - offset; if (send_len == 0) { break; @@ -569,12 +637,14 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { _end_stream = true && end_stream; } - error = !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, body->offset, send_len); + error = !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, offset, send_len); if (!error) { - swoole_trace_log( - SW_TRACE_HTTP2, "body: send length=%zu, stream->remote_window_size=%u", send_len, stream->remote_window_size); + swoole_trace_log(SW_TRACE_HTTP2, + "body: send length=%zu, stream->remote_window_size=%u", + send_len, + stream->remote_window_size); - body->offset += send_len; + offset += send_len; if (send_len > stream->remote_window_size) { stream->remote_window_size = 0; } else { @@ -584,8 +654,63 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { } } - if (!error && ztrailer && !stream->send_trailer()) { - error = true; + return !error; +} + +static bool http2_server_write(HttpContext *ctx, const String *chunk) { + Http2Session *client = http2_sessions[ctx->fd]; + Http2Stream *stream = ctx->stream; + + ctx->send_chunked = 1; + + if (!ctx->send_header_ && !stream->send_header(nullptr, false)) { + return false; + } + + if (!http2_server_send_data(ctx, client, stream, chunk, false)) { + return false; + } + + return true; +} + +static bool http2_server_respond(HttpContext *ctx, const String *body) { + Http2Session *client = http2_sessions[ctx->fd]; + Http2Stream *stream = ctx->stream; + + zval *ztrailer = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + if (php_swoole_array_length_safe(ztrailer) == 0) { + ztrailer = nullptr; + } + + bool end_stream = (ztrailer == nullptr); + + if (!ctx->send_header_ && !stream->send_header(body, end_stream)) { + return false; + } + + // The headers has already been sent, retries are no longer allowed (even if send body failed) + ctx->end_ = 1; + + bool error = true; + +#ifdef SW_HAVE_COMPRESSION + if (ctx->content_compressed) { + body = ctx->zlib_buffer.get(); + } +#endif + + SW_LOOP { + if (ctx->send_chunked && body->length == 0 && !stream->send_end_stream_data_frame()) { + break; + } else if (!http2_server_send_data(ctx, client, stream, body, end_stream)) { + break; + } else if (ztrailer && !stream->send_trailer()) { + break; + } + error = false; + break; } if (error) { @@ -602,20 +727,141 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { return !error; } -static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { +static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler) { Http2Session *client = http2_sessions[ctx->fd]; - Http2Stream *stream = (Http2Stream *) ctx->stream; std::shared_ptr body; #ifdef SW_HAVE_COMPRESSION ctx->accept_compression = 0; +#endif + bool error = false; + zval *ztrailer = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + if (php_swoole_array_length_safe(ztrailer) == 0) { + ztrailer = nullptr; + } + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("content-type"))) { + ctx->set_header(ZEND_STRL("content-type"), handler->get_content_type(), strlen(handler->get_content_type()), 0); + } + + bool end_stream = (ztrailer == nullptr); + body.reset(new String()); + body->length = handler->get_content_length(); + if (!ctx->stream->send_header(body.get(), end_stream)) { + return false; + } + + /* headers has already been sent, retries are no longer allowed (even if send body failed) */ + ctx->end_ = 1; + + auto tasks = handler->get_tasks(); + if (!tasks.empty()) { + File fp(handler->get_filename(), O_RDONLY); + if (!fp.ready()) { + return false; + } + + char *buf; + if (tasks.size() > 1) { + for (auto i = tasks.begin(); i != tasks.end(); i++) { + body.reset(new String(i->part_header, strlen(i->part_header))); + if (!ctx->stream->send_body( + body.get(), false, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + break; + } else { + client->remote_window_size -= body->length; // TODO: flow control? + } + + fp.set_offset(i->offset); + buf = (char *) emalloc(i->length); + auto n_reads = fp.read(buf, i->length); + if (n_reads < 0) { + efree(buf); + return false; + } + body.reset(new String(buf, i->length)); + efree(buf); + if (!ctx->stream->send_body( + body.get(), false, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + break; + } else { + client->remote_window_size -= body->length; // TODO: flow control? + } + } + + if (!error) { + body.reset(new String(handler->get_end_part(), strlen(handler->get_end_part()))); + if (!ctx->stream->send_body( + body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + } else { + client->remote_window_size -= body->length; // TODO: flow control? + } + } + } else if (tasks[0].length > 0) { + auto callback = [&]() -> bool { + fp.set_offset(tasks[0].offset); + buf = (char *) emalloc(tasks[0].length); + auto n_reads = fp.read(buf, tasks[0].length); + if (n_reads < 0) { + efree(buf); + return false; + } + body.reset(new String(buf, n_reads)); + efree(buf); + return true; + }; + if (swoole_coroutine_is_in()) { + if (!swoole::coroutine::async(callback)) { + return false; + } + } else { + if (!callback()) { + return false; + } + } + if (!ctx->stream->send_body( + body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + } else { + client->remote_window_size -= body->length; // TODO: flow control? + } + } + } + + if (!error && ztrailer) { + if (!ctx->stream->send_trailer()) { + error = true; + } + } + + if (error) { + ctx->close(ctx); + } else { + client->streams.erase(ctx->stream->id); + delete ctx->stream; + } + + return true; +} + +bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { + Http2Session *client = http2_sessions[fd]; + std::shared_ptr body; + +#ifdef SW_HAVE_COMPRESSION + accept_compression = 0; #endif if (swoole_coroutine_is_in()) { body = System::read_file(file, false); if (!body) { return false; } - if (!ctx->stream) { + if (!stream) { /* closed */ return false; } @@ -625,28 +871,29 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t return false; } body = fp.read_content(); - if (body->empty()) { - return false; - } } body->length = SW_MIN(length, body->length); zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); if (php_swoole_array_length_safe(ztrailer) == 0) { ztrailer = nullptr; } - const char *mimetype = swoole::mime_type::get(file).c_str(); - ctx->set_header(ZEND_STRL("content-type"), mimetype, strlen(mimetype), 0); + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("content-type"))) { + const char *mimetype = swoole::mime_type::get(file).c_str(); + set_header(ZEND_STRL("content-type"), mimetype, strlen(mimetype), 0); + } bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(length, end_stream)) { + if (!stream->send_header(body.get(), end_stream)) { return false; } /* headers has already been sent, retries are no longer allowed (even if send body failed) */ - ctx->end_ = 1; + end_ = 1; bool error = false; @@ -654,7 +901,7 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t if (!stream->send_body(body.get(), end_stream, client->local_settings.max_frame_size, offset, length)) { error = true; } else { - client->remote_settings.window_size -= length; // TODO: flow control? + client->remote_window_size -= length; // TODO: flow control? } } @@ -665,7 +912,7 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t } if (error) { - ctx->close(ctx); + close(this); } else { client->streams.erase(stream->id); delete stream; @@ -674,7 +921,20 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t return true; } -static int http2_parse_header(Http2Session *client, HttpContext *ctx, int flags, const char *in, size_t inlen) { +static bool http2_server_onBeforeRequest(HttpContext *ctx) { + Server *serv = (Server *) ctx->private_data; + if (serv->is_unavailable()) { + String null_body{}; + ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; + http2_server_respond(ctx, &null_body); + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + return false; + } + return swoole_http_server_onBeforeRequest(ctx); +} + +static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int flags, const char *in, size_t inlen) { nghttp2_hd_inflater *inflater = client->inflater; if (!inflater) { @@ -757,15 +1017,17 @@ static int http2_parse_header(Http2Session *client, HttpContext *ctx, int flags, } } else { if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-type")) { - if (SW_STRCASECT((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { + if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; - } else if (SW_STRCASECT((char *) nv.value, nv.valuelen, "multipart/form-data")) { - int boundary_len = nv.valuelen - (sizeof("multipart/form-data; boundary=") - 1); - if (boundary_len <= 0) { - swoole_warning("invalid multipart/form-data body fd:%ld", ctx->fd); + } else if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "multipart/form-data")) { + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!ctx->get_multipart_boundary( + (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) { return SW_ERR; } - ctx->parse_form_data((char *) nv.value + nv.valuelen - boundary_len, boundary_len); + ctx->init_multipart_parser(boundary_str, boundary_len); ctx->parser.data = ctx; } } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "cookie")) { @@ -824,14 +1086,14 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swoole_http2_frame_trace_log(recv, "ACK"); + swoole_http2_frame_trace_log("ACK"); break; } while (length > 0) { id = ntohs(*(uint16_t *) (buf)); value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); - swoole_http2_frame_trace_log(recv, "id=%d, value=%d", id, value); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); switch (id) { case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: if (value != client->remote_settings.header_table_size) { @@ -853,7 +1115,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); break; case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - client->remote_settings.window_size = value; + client->remote_window_size = client->remote_settings.init_window_size = value; swoole_trace_log(SW_TRACE_HTTP2, "setting: init_window_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: @@ -861,7 +1123,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - client->remote_settings.max_header_list_size = value; // useless now + client->remote_settings.max_header_list_size = value; // useless now swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); break; default: @@ -876,7 +1138,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } case SW_HTTP2_TYPE_HEADERS: { stream = client->streams[stream_id]; - swoole_http2_frame_trace_log(recv, "%s", (stream ? "exist stream" : "new stream")); + swoole_http2_frame_trace_log("%s", (stream ? "exist stream" : "new stream")); HttpContext *ctx; if (!stream) { stream = new Http2Stream(client, stream_id); @@ -892,7 +1154,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } else { ctx = stream->ctx; } - if (http2_parse_header(client, ctx, flags, buf, length) < 0) { + if (http2_server_parse_header(client, ctx, flags, buf, length) < 0) { return SW_ERR; } @@ -904,7 +1166,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_DATA: { - swoole_http2_frame_trace_log(recv, "data"); + swoole_http2_frame_trace_log("data"); auto stream_iterator = client->streams.find(stream_id); if (stream_iterator == client->streams.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NOT_FOUND, "http2 stream#%d not found", stream_id); @@ -924,17 +1186,19 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buffer->append(buf, length); // flow control - client->local_settings.window_size -= length; + client->local_window_size -= length; stream->local_window_size -= length; if (length > 0) { - if (client->local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, 0, SW_HTTP2_MAX_WINDOW_SIZE - client->local_settings.window_size); - client->local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE; + if (client->local_window_size < (client->local_settings.init_window_size / 4)) { + http2_server_send_window_update( + ctx, 0, client->local_settings.init_window_size - client->local_window_size); + client->local_window_size = client->local_settings.init_window_size; } - if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size); - stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE; + if (stream->local_window_size < (client->local_settings.init_window_size / 4)) { + http2_server_send_window_update( + ctx, stream_id, client->local_settings.init_window_size - stream->local_window_size); + stream->local_window_size = client->local_settings.init_window_size; } } @@ -946,11 +1210,8 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); } else if (ctx->mt_parser != nullptr) { - multipart_parser *multipart_parser = ctx->mt_parser; - size_t n = multipart_parser_execute(multipart_parser, buffer->str, buffer->length); - if (n != (size_t) length) { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, n=%zu", n); + if (!ctx->parse_multipart_data(buffer->str, buffer->length)) { + return SW_ERR; } } @@ -963,7 +1224,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_PING: { - swoole_http2_frame_trace_log(recv, "ping"); + swoole_http2_frame_trace_log("ping"); if (!(flags & SW_HTTP2_FLAG_ACK)) { char ping_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; Http2::set_frame_header( @@ -977,7 +1238,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { case SW_HTTP2_TYPE_WINDOW_UPDATE: { value = ntohl(*(uint32_t *) buf); if (stream_id == 0) { - client->remote_settings.window_size += value; + client->remote_window_size += value; } else { if (client->streams.find(stream_id) != client->streams.end()) { stream = client->streams[stream_id]; @@ -990,12 +1251,12 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } } } - swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value); + swoole_http2_frame_trace_log("window_size_increment=%d", value); break; } case SW_HTTP2_TYPE_RST_STREAM: { value = ntohl(*(int *) (buf)); - swoole_http2_frame_trace_log(recv, "error_code=%d", value); + swoole_http2_frame_trace_log("error_code=%d", value); if (client->streams.find(stream_id) != client->streams.end()) { // TODO: i onRequest and use request->recv // stream exist @@ -1010,8 +1271,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swoole_http2_frame_trace_log(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", + swoole_http2_frame_trace_log("last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", server_last_stream_id, value, (int) (length - SW_HTTP2_GOAWAY_SIZE), @@ -1022,16 +1282,13 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } default: { - swoole_http2_frame_trace_log(recv, ""); + swoole_http2_frame_trace_log(""); } } return retval; } -/** - * Http2 - */ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) { int session_id = req->info.fd; Http2Session *client = http2_sessions[session_id]; @@ -1039,7 +1296,7 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client = new Http2Session(session_id); } - client->handle = swoole_http2_onRequest; + client->handle = http2_server_onRequest; if (!client->default_ctx) { client->default_ctx = new HttpContext(); client->default_ctx->init(serv); @@ -1047,7 +1304,7 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client->default_ctx->http2 = true; client->default_ctx->stream = (Http2Stream *) -1; client->default_ctx->keepalive = true; - client->default_ctx->sendfile = http2_context_sendfile; + client->default_ctx->onBeforeRequest = http2_server_onBeforeRequest; } zval zdata; @@ -1075,8 +1332,15 @@ void HttpContext::http2_end(zval *zdata, zval *return_value) { http_body.length = 0; http_body.str = nullptr; } - - RETURN_BOOL(swoole_http2_server_respond(this, &http_body)); + RETURN_BOOL(http2_server_respond(this, &http_body)); } -#endif +void HttpContext::http2_write(zval *zdata, zval *return_value) { + String http_body = {}; + http_body.length = php_swoole_get_send_data(zdata, &http_body.str); + if (http_body.length == 0) { + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); + RETURN_FALSE; + } + RETURN_BOOL(http2_server_write(this, &http_body)); +} diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 4456cc2ca95..4f1eff0847e 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Author: Twosee | | Author: Fang | | Author: Yuanyi Zhi | @@ -32,19 +32,11 @@ SW_EXTERN_C_BEGIN #include "thirdparty/swoole_http_parser.h" +#include "stubs/php_swoole_http_client_coro_arginfo.h" #include "ext/standard/base64.h" -#ifdef SW_HAVE_ZLIB -#include -#endif - SW_EXTERN_C_END - -#ifdef SW_HAVE_BROTLI -#include -#endif - using swoole::File; using swoole::String; using swoole::coroutine::Socket; @@ -52,15 +44,6 @@ using swoole::network::Address; namespace WebSocket = swoole::websocket; -enum http_client_error_status_code { - HTTP_CLIENT_ESTATUS_CONNECT_FAILED = -1, - HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT = -2, - HTTP_CLIENT_ESTATUS_SERVER_RESET = -3, - HTTP_CLIENT_ESTATUS_SEND_FAILED = -4, -}; - -extern void php_swoole_client_coro_socket_free(Socket *cli); - static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_headers_complete(swoole_http_parser *parser); @@ -68,8 +51,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ static int http_parser_on_message_complete(swoole_http_parser *parser); // clang-format off -static const swoole_http_parser_settings http_parser_settings = -{ +static const swoole_http_parser_settings http_parser_settings = { nullptr, nullptr, nullptr, @@ -85,17 +67,27 @@ static const swoole_http_parser_settings http_parser_settings = namespace swoole { namespace coroutine { -class HttpClient { +namespace http { +enum StatusCode { + ESTATUS_CONNECT_FAILED = -1, + ESTATUS_REQUEST_TIMEOUT = -2, + ESTATUS_SERVER_RESET = -3, + ESTATUS_SEND_FAILED = -4, +}; + +class Client { public: /* request info */ - std::string host = "127.0.0.1"; - uint16_t port = 80; + std::string host; + uint16_t port; #ifdef SW_USE_OPENSSL - uint8_t ssl = false; + uint8_t ssl; #endif - double connect_timeout = network::Socket::default_connect_timeout; + double connect_timeout = 0; + double response_timeout = 0; bool defer = false; bool lowercase_header = true; + bool use_default_port; int8_t method = SW_HTTP_GET; std::string path; @@ -106,13 +98,12 @@ class HttpClient { int tmp_header_field_name_len = 0; String *body = nullptr; #ifdef SW_HAVE_COMPRESSION - enum http_compress_method compress_method = HTTP_COMPRESS_NONE; + enum swHttpCompressMethod compress_method = HTTP_COMPRESS_NONE; bool compression_error = false; #endif /* options */ - uint8_t reconnect_interval = 1; - uint8_t reconnected_count = 0; + uint8_t max_retries = 0; bool keep_alive = true; // enable by default bool websocket = false; // if upgrade successfully bool chunked = false; // Transfer-Encoding: chunked @@ -120,19 +111,33 @@ class HttpClient { bool body_decompression = true; bool http_compression = true; #ifdef SW_HAVE_ZLIB - bool websocket_compression = false; // allow to compress websocket messages + bool websocket_compression = false; // allow to compress websocket messages + bool accept_websocket_compression = false; // websocket server accepts compression #endif - File *download_file = nullptr; // save http response to file - zend::String download_file_name; // unlink the file on error - zend_long download_offset = 0; + bool in_callback = false; bool has_upload_files = false; + std::shared_ptr download_file; // save http response to file + zend::String download_file_name; // unlink the file on error + zend_long download_offset = 0; + /* safety zval */ zval _zobject; zval *zobject = &_zobject; + zval zsocket; + zend::Callable *write_func = nullptr; String *tmp_write_buffer = nullptr; + bool connection_close = false; - HttpClient(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); + Client(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); + + bool is_available() { + if (sw_unlikely(!socket || !socket->is_connected())) { + php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION); + return false; + } + return true; + } private: #ifdef SW_HAVE_ZLIB @@ -141,28 +146,31 @@ class HttpClient { #endif #ifdef SW_HAVE_BROTLI BrotliDecoderState *brotli_decoder_state = nullptr; +#endif +#ifdef SW_HAVE_ZSTD + ZSTD_DStream *zstd_stream = nullptr; #endif bool bind(std::string address, int port = 0); bool connect(); void set_error(int error, const char *msg, int status); bool keep_liveness(); - bool send(); + bool send_request(); void reset(); - static inline void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) { + static void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) { buf->append(key, key_len); buf->append(ZEND_STRL(": ")); buf->append(data, data_len); buf->append(ZEND_STRL("\r\n")); } - static inline void add_content_length(String *buf, int length) { - char content_length_str[32]; - int n = snprintf(SW_STRS(content_length_str), "Content-Length: %d\r\n\r\n", length); + static void add_content_length(String *buf, size_t length) { + char content_length_str[64]; + size_t n = sw_snprintf(SW_STRS(content_length_str), "Content-Length: %zu\r\n\r\n", length); buf->append(content_length_str, n); } - static inline void create_token(int length, char *buf) { + static void create_token(int length, char *buf) { char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}"; int i; assert(length < 1024); @@ -179,12 +187,13 @@ class HttpClient { void apply_setting(zval *zset, const bool check_all = true); void set_basic_auth(const std::string &username, const std::string &password); bool exec(std::string _path); - bool recv(double timeout = 0); - void recv(zval *zframe, double timeout = 0); - bool recv_http_response(double timeout = 0); + bool recv_response(double timeout = 0); + bool recv_websocket_frame(zval *zframe, double timeout = 0); + void add_header(const char *key, size_t key_len, const char *str, size_t length); bool upgrade(std::string path); bool push(zval *zdata, zend_long opcode = websocket::OPCODE_TEXT, uint8_t flags = websocket::FLAG_FIN); bool close(const bool should_be_reset = true); + void socket_dtor(); void get_header_out(zval *return_value) { String *buffer = nullptr; @@ -208,11 +217,13 @@ class HttpClient { void getsockname(zval *return_value) { Address sa; - if (!socket || !socket->getsockname(&sa)) { - ZVAL_FALSE(return_value); - return; + if (!is_available()) { + RETURN_FALSE; + } + if (!socket->getsockname(&sa)) { + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } - array_init(return_value); add_assoc_string(return_value, "address", (char *) sa.get_ip()); add_assoc_long(return_value, "port", sa.get_port()); @@ -220,11 +231,13 @@ class HttpClient { void getpeername(zval *return_value) { Address sa; - if (!socket || !socket->getpeername(&sa)) { - ZVAL_FALSE(return_value); - return; + if (!is_available()) { + RETURN_FALSE; + } + if (!socket->getpeername(&sa)) { + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } - array_init(return_value); add_assoc_string(return_value, "address", (char *) sa.get_ip()); add_assoc_long(return_value, "port", sa.get_port()); @@ -232,25 +245,30 @@ class HttpClient { #ifdef SW_USE_OPENSSL void getpeercert(zval *return_value) { + if (!is_available()) { + RETURN_FALSE; + } auto cert = socket->ssl_get_peer_cert(); if (cert.empty()) { - ZVAL_FALSE(return_value); - return; + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } else { - ZVAL_STRINGL(return_value, cert.c_str(), cert.length()); + RETURN_STRINGL(cert.c_str(), cert.length()); } } #endif - ~HttpClient(); + ~Client(); private: Socket *socket = nullptr; - swSocketType socket_type = SW_SOCK_TCP; + NameResolver::Context resolve_context_ = {}; + SocketType socket_type = SW_SOCK_TCP; swoole_http_parser parser = {}; - bool wait = false; + bool wait_response = false; }; +} // namespace http } // namespace coroutine } // namespace swoole @@ -260,10 +278,10 @@ static zend_object_handlers swoole_http_client_coro_handlers; static zend_class_entry *swoole_http_client_coro_exception_ce; static zend_object_handlers swoole_http_client_coro_exception_handlers; -using swoole::coroutine::HttpClient; +using swoole::coroutine::http::Client; struct HttpClientObject { - HttpClient *phc; + Client *client; zend_object std; }; @@ -301,131 +319,44 @@ static PHP_METHOD(swoole_http_client_coro, close); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, ssl) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setDefer, 0, 0, 0) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setMethod, 0, 0, 1) - ZEND_ARG_INFO(0, method) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setHeaders, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, headers, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setBasicAuth, 0, 0, 2) - ZEND_ARG_INFO(0, username) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setCookies, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, cookies, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setData, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_addFile, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_addData, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_execute, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_get, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_post, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_download, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, file) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_upgrade, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() static const zend_function_entry swoole_http_client_coro_methods[] = { - PHP_ME(swoole_http_client_coro, __construct, arginfo_swoole_http_client_coro_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, set, arginfo_swoole_http_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setDefer, arginfo_swoole_http_client_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setMethod, arginfo_swoole_http_client_coro_setMethod, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setHeaders, arginfo_swoole_http_client_coro_setHeaders, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setBasicAuth, arginfo_swoole_http_client_coro_setBasicAuth, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setCookies, arginfo_swoole_http_client_coro_setCookies, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setData, arginfo_swoole_http_client_coro_setData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, addFile, arginfo_swoole_http_client_coro_addFile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, addData, arginfo_swoole_http_client_coro_addData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, execute, arginfo_swoole_http_client_coro_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getsockname, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, get, arginfo_swoole_http_client_coro_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, post, arginfo_swoole_http_client_coro_post, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, download, arginfo_swoole_http_client_coro_download, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getBody, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getHeaders, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getCookies, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getStatusCode, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getHeaderOut, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Http_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, set, arginfo_class_Swoole_Coroutine_Http_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getDefer, arginfo_class_Swoole_Coroutine_Http_Client_getDefer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setDefer, arginfo_class_Swoole_Coroutine_Http_Client_setDefer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setMethod, arginfo_class_Swoole_Coroutine_Http_Client_setMethod, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setHeaders, arginfo_class_Swoole_Coroutine_Http_Client_setHeaders, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setBasicAuth, arginfo_class_Swoole_Coroutine_Http_Client_setBasicAuth, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setCookies, arginfo_class_Swoole_Coroutine_Http_Client_setCookies, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setData, arginfo_class_Swoole_Coroutine_Http_Client_setData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, addFile, arginfo_class_Swoole_Coroutine_Http_Client_addFile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, addData, arginfo_class_Swoole_Coroutine_Http_Client_addData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, execute, arginfo_class_Swoole_Coroutine_Http_Client_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getpeername, arginfo_class_Swoole_Coroutine_Http_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getsockname, arginfo_class_Swoole_Coroutine_Http_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, get, arginfo_class_Swoole_Coroutine_Http_Client_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, post, arginfo_class_Swoole_Coroutine_Http_Client_post, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, download, arginfo_class_Swoole_Coroutine_Http_Client_download, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getBody, arginfo_class_Swoole_Coroutine_Http_Client_getBody, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getHeaders, arginfo_class_Swoole_Coroutine_Http_Client_getHeaders, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getCookies, arginfo_class_Swoole_Coroutine_Http_Client_getCookies, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getStatusCode, arginfo_class_Swoole_Coroutine_Http_Client_getStatusCode, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getHeaderOut, arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_http_client_coro, upgrade, arginfo_swoole_http_client_coro_upgrade, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, push, arginfo_swoole_http_client_coro_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, recv, arginfo_swoole_http_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, upgrade, arginfo_class_Swoole_Coroutine_Http_Client_upgrade, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, push, arginfo_class_Swoole_Coroutine_Http_Client_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, recv, arginfo_class_Swoole_Coroutine_Http_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, close, arginfo_class_Swoole_Coroutine_Http_Client_close, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on -void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) { +void php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) { const char *p, *eof = at + length; size_t key_len = 0, value_len = 0; zval zvalue; @@ -461,28 +392,29 @@ void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval } static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; http->tmp_header_field_name = (char *) at; http->tmp_header_field_name_len = length; return 0; } static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; zval *zobject = (zval *) http->zobject; - zval *zheaders = - sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); + char *header_name = http->tmp_header_field_name; size_t header_len = http->tmp_header_field_name_len; + zend::CharPtr _header_name; if (http->lowercase_header) { - header_name = zend_str_tolower_dup(header_name, header_len); + _header_name.assign_tolower(header_name, header_len); + header_name = _header_name.get(); } - add_assoc_stringl_ex(zheaders, header_name, header_len, (char *) at, length); + http->add_header(header_name, header_len, (char *) at, length); if (parser->status_code == SW_HTTP_SWITCHING_PROTOCOLS && SW_STREQ(header_name, header_len, "upgrade")) { - if (SW_STRCASEEQ(at, length, "websocket")) { + if (swoole_http_token_list_contains_value(at, length, "websocket")) { http->websocket = true; } /* TODO: protocol error? */ @@ -490,9 +422,8 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a #ifdef SW_HAVE_ZLIB else if (http->websocket && http->websocket_compression && SW_STREQ(header_name, header_len, "sec-websocket-extensions")) { - if (SW_STRCASECT(at, length, "permessage-deflate") && SW_STRCASECT(at, length, "client_no_context_takeover") && - SW_STRCASECT(at, length, "server_no_context_takeover")) { - http->websocket_compression = true; + if (swoole_strncasestr(at, length, SW_STRL("permessage-deflate"))) { + http->accept_websocket_compression = true; } } #endif @@ -501,56 +432,77 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("cookies"), 0); zval *zset_cookie_headers = sw_zend_read_and_convert_property_array( swoole_http_client_coro_ce, zobject, ZEND_STRL("set_cookie_headers"), 0); - http_parse_set_cookies(at, length, zcookies, zset_cookie_headers); + php_swoole_http_parse_set_cookies(at, length, zcookies, zset_cookie_headers); } #ifdef SW_HAVE_COMPRESSION else if (SW_STREQ(header_name, header_len, "content-encoding")) { if (0) { } #ifdef SW_HAVE_BROTLI - else if (SW_STRCASECT(at, length, "br")) { + else if (SW_STR_ISTARTS_WITH(at, length, "br")) { http->compress_method = HTTP_COMPRESS_BR; } #endif #ifdef SW_HAVE_ZLIB - else if (SW_STRCASECT(at, length, "gzip")) { + else if (SW_STR_ISTARTS_WITH(at, length, "gzip")) { http->compress_method = HTTP_COMPRESS_GZIP; - } else if (SW_STRCASECT(at, length, "deflate")) { + } else if (SW_STR_ISTARTS_WITH(at, length, "deflate")) { http->compress_method = HTTP_COMPRESS_DEFLATE; } +#endif +#ifdef SW_HAVE_ZSTD + else if (SW_STR_ISTARTS_WITH(at, length, "zstd")) { + http->compress_method = HTTP_COMPRESS_ZSTD; + } #endif } #endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) { + else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { http->chunked = true; - } - - if (http->lowercase_header) { - efree(header_name); + } else if (SW_STREQ(header_name, header_len, "connection")) { + http->connection_close = SW_STR_ISTARTS_WITH(at, length, "close"); } return 0; } static int http_parser_on_headers_complete(swoole_http_parser *parser) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; if (http->method == SW_HTTP_HEAD || parser->status_code == SW_HTTP_NO_CONTENT) { return 1; } return 0; } +static inline ssize_t http_client_co_write(int sockfd, const void *buf, size_t count) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_write(sockfd, buf, count); +#else + return swoole_coroutine_write(sockfd, buf, count); +#endif +} + static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; + if (http->write_func) { + zval zargv[2]; + zargv[0] = *http->zobject; + ZVAL_STRINGL(&zargv[1], at, length); + http->in_callback = true; + bool success = http->write_func->call(2, zargv, nullptr); + http->in_callback = false; + zval_ptr_dtor(&zargv[1]); + return success ? 0 : -1; + } #ifdef SW_HAVE_COMPRESSION - if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) { + else if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) { if (!http->decompress_response(at, length)) { http->compression_error = true; goto _append_raw; } - } else + } #endif - { + else { #ifdef SW_HAVE_COMPRESSION _append_raw: #endif @@ -561,25 +513,25 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ if (http->download_file_name.get() && http->body->length > 0) { if (http->download_file == nullptr) { char *download_file_name = http->download_file_name.val(); - std::unique_ptr fp(new File(download_file_name, O_CREAT | O_WRONLY, 0664)); + std::shared_ptr fp = std::make_shared(download_file_name, O_CREAT | O_WRONLY, 0664); if (!fp->ready()) { swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); - return false; + return -1; } if (http->download_offset == 0) { if (!fp->truncate(0)) { swoole_sys_warning("ftruncate(%s) failed", download_file_name); - return false; + return -1; } } else { - if (!fp->set_offest(http->download_offset)) { + if (!fp->set_offset(http->download_offset)) { swoole_sys_warning("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); - return false; + return -1; } } - http->download_file = fp.release(); + http->download_file = fp; } - if (swoole_coroutine_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != + if (http_client_co_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != (ssize_t) http->body->length) { return -1; } @@ -589,7 +541,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ } static int http_parser_on_message_complete(swoole_http_parser *parser) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; zval *zobject = (zval *) http->zobject; if (parser->upgrade && !http->websocket) { @@ -615,9 +567,13 @@ static int http_parser_on_message_complete(swoole_http_parser *parser) { } } -HttpClient::HttpClient(zval *zobject, std::string host, zend_long port, zend_bool ssl) { +Client::Client(zval *zobject, std::string host, zend_long port, zend_bool ssl) { this->socket_type = network::Socket::convert_to_type(host); this->host = host; + this->use_default_port = port == 0; + if (this->use_default_port) { + port = ssl ? 443 : 80; + } this->port = port; #ifdef SW_USE_OPENSSL this->ssl = ssl; @@ -627,7 +583,7 @@ HttpClient::HttpClient(zval *zobject, std::string host, zend_long port, zend_boo } #ifdef SW_HAVE_COMPRESSION -bool HttpClient::decompress_response(const char *in, size_t in_len) { +bool Client::decompress_response(const char *in, size_t in_len) { if (in_len == 0) { return false; } @@ -731,7 +687,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { } } else { swoole_warning("BrotliDecoderDecompressStream() failed, %s", - BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); + BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); break; } } @@ -739,6 +695,48 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { body->length = reserved_body_length; return false; } +#endif +#ifdef SW_HAVE_ZSTD + case HTTP_COMPRESS_ZSTD: { + size_t zstd_result = 0; + if (zstd_stream == nullptr) { + zstd_stream = ZSTD_createDStream(); + if (!zstd_stream) { + swoole_warning("ZSTD_createDStream() failed, can not create ZSTD stream"); + return false; + } + + zstd_result = ZSTD_initDStream(zstd_stream); + if (ZSTD_isError(zstd_result)) { + swoole_warning("ZSTD_initDStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result)); + return false; + } + } + + size_t recommended_size = ZSTD_DStreamOutSize(); + ZSTD_inBuffer in_buffer = {in, in_len, 0}; + ZSTD_outBuffer out_buffer = {body->str + body->length, body->size - body->length, 0}; + while (in_buffer.pos < in_buffer.size) { + if (sw_unlikely(out_buffer.pos == out_buffer.size)) { + if (!body->extend(recommended_size + body->size)) { + swoole_warning("ZSTD_decompressStream() failed, no memory is available"); + return false; + } + + body->length += out_buffer.pos; + out_buffer = {body->str + body->length, body->size - body->length, 0}; + } + + zstd_result = ZSTD_decompressStream(zstd_stream, &out_buffer, &in_buffer); + if (ZSTD_isError(zstd_result)) { + swoole_warning("ZSTD_decompressStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result)); + return false; + } + } + + body->length += out_buffer.pos; + return true; + } #endif default: break; @@ -749,7 +747,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { } #endif -void HttpClient::apply_setting(zval *zset, const bool check_all) { +void Client::apply_setting(zval *zset, const bool check_all) { if (!ZVAL_IS_ARRAY(zset) || php_swoole_array_length(zset) == 0) { return; } @@ -757,12 +755,14 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { zval *ztmp; HashTable *vht = Z_ARRVAL_P(zset); - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp) || - php_swoole_array_get_value(vht, "timeout", ztmp) /* backward compatibility */) { + if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { connect_timeout = zval_get_double(ztmp); } - if (php_swoole_array_get_value(vht, "reconnect", ztmp)) { - reconnect_interval = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); + if (php_swoole_array_get_value(vht, "timeout", ztmp)) { + response_timeout = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "max_retries", ztmp)) { + max_retries = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); } if (php_swoole_array_get_value(vht, "defer", ztmp)) { defer = zval_is_true(ztmp); @@ -787,9 +787,15 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { websocket_compression = zval_is_true(ztmp); } #endif + if (php_swoole_array_get_value(vht, "write_func", ztmp)) { + if (write_func) { + delete write_func; + } + write_func = sw_callable_create(ztmp); + } } if (socket) { - php_swoole_client_set(socket, zset); + php_swoole_socket_set(socket, zset); #ifdef SW_USE_OPENSSL if (socket->http_proxy && !socket->ssl_is_enable()) #else @@ -801,7 +807,7 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { } } -void HttpClient::set_basic_auth(const std::string &username, const std::string &password) { +void Client::set_basic_auth(const std::string &username, const std::string &password) { std::string input = username + ":" + password; size_t output_size = sizeof("Basic ") + BASE64_ENCODE_OUT_SIZE(input.size()); char *output = (char *) emalloc(output_size); @@ -813,50 +819,119 @@ void HttpClient::set_basic_auth(const std::string &username, const std::string & } } -bool HttpClient::connect() { - if (!socket) { - if (!body) { - body = new String(SW_HTTP_RESPONSE_INIT_SIZE); - if (!body) { - set_error(ENOMEM, swoole_strerror(ENOMEM), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - return false; - } +static zend_always_inline zval *sw_zend_symtable_str_add( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) { + if (numeric_key) { + return zend_hash_index_add(ht, idx, pData); + } else { + return zend_hash_str_add(ht, str, len, pData); + } +} + +static zend_always_inline zval *sw_zend_symtable_str_find( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key) { + if (numeric_key) { + return zend_hash_index_find(ht, idx); + } else { + return zend_hash_str_find(ht, str, len); + } +} + +static zend_always_inline zval *sw_zend_symtable_str_update( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) { + if (numeric_key) { + return zend_hash_index_update(ht, idx, pData); + } else { + return zend_hash_str_update(ht, str, len, pData); + } +} + +void Client::add_header(const char *key, size_t key_len, const char *str, size_t length) { + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); + zend_array *array = Z_ARRVAL_P(zheaders); + + zval zvalue_new; + ZVAL_STRINGL(&zvalue_new, str, length); + + zend_ulong idx; + bool numeric_key = ZEND_HANDLE_NUMERIC_STR(key, key_len, idx); + + zval *zresult = sw_zend_symtable_str_add(array, key, key_len, idx, numeric_key, &zvalue_new); + /** + * Adding failed, indicating that this header already exists and must be converted to an array + */ + if (!zresult) { + zval *zvalue_found = sw_zend_symtable_str_find(array, key, key_len, idx, numeric_key); + if (ZVAL_IS_ARRAY(zvalue_found)) { + add_next_index_zval(zvalue_found, &zvalue_new); + } else { + zval zvalue_array; + array_init_size(&zvalue_array, 2); + Z_ADDREF_P(zvalue_found); + add_next_index_zval(&zvalue_array, zvalue_found); + add_next_index_zval(&zvalue_array, &zvalue_new); + sw_zend_symtable_str_update(array, key, key_len, idx, numeric_key, &zvalue_array); } + } +} - php_swoole_check_reactor(); - socket = new Socket(socket_type); - if (UNEXPECTED(socket->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - set_error(errno, swoole_strerror(errno), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - delete socket; - socket = nullptr; +bool Client::connect() { + if (socket) { + return true; + } + if (!body) { + body = new String(SW_HTTP_RESPONSE_INIT_SIZE); + if (!body) { + set_error(ENOMEM, swoole_strerror(ENOMEM), ESTATUS_CONNECT_FAILED); return false; } + } + + php_swoole_check_reactor(); + auto object = php_swoole_create_socket(socket_type); + if (UNEXPECTED(!object)) { + set_error(errno, swoole_strerror(errno), ESTATUS_CONNECT_FAILED); + return false; + } + ZVAL_OBJ(&zsocket, object); + socket = php_swoole_get_socket(&zsocket); + #ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } + if (ssl && !socket->enable_ssl_encrypt()) { + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); + close(); + return false; + } #endif - // apply settings - apply_setting( - sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), - false); - - // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); - // connect - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - if (!socket->connect(host, port)) { - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - close(); - return false; - } - reconnected_count = 0; - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); + + // apply settings + apply_setting(sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), false); + + // reset the properties that depend on the connection + websocket = false; +#ifdef SW_HAVE_ZLIB + accept_websocket_compression = false; +#endif + + double _timeout = connect_timeout == 0 ? network::Socket::default_connect_timeout : connect_timeout; + socket->set_timeout(_timeout, Socket::TIMEOUT_CONNECT); + socket->set_resolve_context(&resolve_context_); + socket->set_dtor([this](Socket *_socket) { socket_dtor(); }); + // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); + + if (!socket->connect(host, port)) { + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); + close(); + return false; } + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &zsocket); + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } -void HttpClient::set_error(int error, const char *msg, int status) { +void Client::set_error(int error, const char *msg, int status) { auto ce = swoole_http_client_coro_ce; auto obj = SW_Z8_OBJ_P(zobject); zend_update_property_long(ce, obj, ZEND_STRL("errCode"), error); @@ -864,15 +939,15 @@ void HttpClient::set_error(int error, const char *msg, int status) { zend_update_property_long(ce, obj, ZEND_STRL("statusCode"), status); } -bool HttpClient::keep_liveness() { +bool Client::keep_liveness() { if (!socket || !socket->check_liveness()) { if (socket) { /* in progress */ socket->check_bound_co(SW_EVENT_RDWR); - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_SERVER_RESET); + set_error(socket->errCode, socket->errMsg, ESTATUS_SERVER_RESET); close(false); } - for (; reconnected_count < reconnect_interval; reconnected_count++) { + SW_LOOP_N(max_retries + 1) { if (connect()) { return true; } @@ -882,31 +957,29 @@ bool HttpClient::keep_liveness() { return true; } -bool HttpClient::send() { +bool Client::send_request() { zval *zvalue = nullptr; uint32_t header_flag = 0x0; zval *zmethod, *zheaders, *zbody, *zupload_files, *zcookies, *z_download_file; if (path.length() == 0) { - php_swoole_fatal_error(E_WARNING, "path is empty"); + php_swoole_socket_set_error_properties(zobject, SW_ERROR_INVALID_PARAMS); return false; } // when new request, clear all properties about the last response - { - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("headers")); - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("set_cookie_headers")); - zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), ""); - } + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("headers")); + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("set_cookie_headers")); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), ""); if (!keep_liveness()) { return false; - } else { - zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); - zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), 0); } + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), 0); + /* another coroutine is connecting */ socket->check_bound_co(SW_EVENT_WRITE); @@ -1037,6 +1110,16 @@ bool HttpClient::send() { // ignore custom Content-Length value continue; } + + if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { +#ifdef SW_HAVE_COMPRESSION + header_flag |= HTTP_HEADER_ACCEPT_ENCODING; +#else + php_swoole_error(E_WARNING, "Missing a compression package, 'Accept-Encoding' is ignored"); + continue; +#endif + } + zend::String str_value(zvalue); add_headers(buffer, key, keylen, str_value.val(), str_value.len()); @@ -1045,8 +1128,6 @@ bool HttpClient::send() { if (SW_STRCASEEQ(str_value.val(), str_value.len(), "close")) { keep_alive = 0; } - } else if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { - header_flag |= HTTP_HEADER_ACCEPT_ENCODING; } } SW_HASHTABLE_FOREACH_END(); @@ -1079,6 +1160,10 @@ bool HttpClient::send() { #else #ifdef SW_HAVE_BROTLI ZEND_STRL("br") +#else +#ifdef SW_HAVE_ZSTD + ZEND_STRL("zstd") +#endif #endif #endif #endif @@ -1105,7 +1190,7 @@ bool HttpClient::send() { buffer->append(key, keylen); buffer->append("=", 1); - int encoded_value_len; + size_t encoded_value_len; encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len); if (encoded_value) { buffer->append(encoded_value, encoded_value_len); @@ -1295,7 +1380,7 @@ bool HttpClient::send() { if (socket->send_all(header_buf, n) != n) { goto _send_fail; } - wait = true; + wait_response = true; return true; } // ============ x-www-form-urlencoded or raw ============ @@ -1333,129 +1418,57 @@ bool HttpClient::send() { } swoole_trace_log(SW_TRACE_HTTP_CLIENT, - "to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <get_fd(), - Coroutine::get_current_cid(), - buffer->length, - (int) buffer->length, - buffer->str); + "to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <get_fd(), + Coroutine::get_current_cid(), + buffer->length, + (int) buffer->length, + buffer->str); if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { _send_fail: - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_SEND_FAILED); + set_error(socket->errCode, socket->errMsg, ESTATUS_SEND_FAILED); close(); return false; } - wait = true; + wait_response = true; return true; } -bool HttpClient::exec(std::string _path) { +bool Client::exec(std::string _path) { path = _path; // bzero when make a new reqeust - reconnected_count = 0; - if (defer) { - return send(); - } else { - return send() && recv(); - } -} - -bool HttpClient::recv(double timeout) { - if (!wait) { - return false; - } - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); - return false; - } - if (!recv_http_response(timeout)) { - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long( - swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - socket->errCode == ETIMEDOUT ? HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT : HTTP_CLIENT_ESTATUS_SERVER_RESET); - close(); - return false; - } - /** - * TODO: Sec-WebSocket-Accept check - */ - if (websocket) { - socket->open_length_check = 1; - socket->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN; - socket->protocol.package_length_offset = 0; - socket->protocol.package_body_offset = 0; - socket->protocol.get_package_length = websocket::get_package_length; - } - // handler keep alive - if (!keep_alive && !websocket) { - close(); - } else { - reset(); - } - - return true; -} - -void HttpClient::recv(zval *zframe, double timeout) { - SW_ASSERT(websocket); - ZVAL_FALSE(zframe); - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); - return; + resolve_context_ = {}; + if (use_default_port) { + resolve_context_.with_port = true; } - - ssize_t retval = socket->recv_packet(timeout); - if (retval <= 0) { - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); - if (socket->errCode != ETIMEDOUT) { - close(); + SW_LOOP_N(max_retries + 1) { + if (send_request() == false) { + return false; } - } else { - String msg; - msg.length = retval; - msg.str = socket->get_read_buffer()->str; -#ifdef SW_HAVE_ZLIB - php_swoole_websocket_frame_unpack_ex(&msg, zframe, websocket_compression); -#else - php_swoole_websocket_frame_unpack(&msg, zframe); -#endif - zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("fd"), socket->get_fd()); + if (defer) { + return true; + } + if (recv_response() == false) { + return false; + } + if (max_retries > 0 && + (parser.status_code == SW_HTTP_BAD_GATEWAY || parser.status_code == SW_HTTP_SERVICE_UNAVAILABLE)) { + close(true); + continue; + } + return true; } + return false; } -bool HttpClient::recv_http_response(double timeout) { +bool Client::recv_response(double timeout) { + if (!wait_response) { + return false; + } ssize_t retval = 0; size_t total_bytes = 0, parsed_n = 0; String *buffer = socket->get_read_buffer(); @@ -1467,12 +1480,13 @@ bool HttpClient::recv_http_response(double timeout) { parser.data = this; if (timeout == 0) { - timeout = socket->get_timeout(Socket::TIMEOUT_READ); + timeout = response_timeout == 0 ? network::Socket::default_read_timeout : response_timeout; } - Socket::timeout_controller tc(socket, timeout, Socket::TIMEOUT_READ); + Socket::TimeoutController tc(socket, timeout, Socket::TIMEOUT_READ); + bool success = false; while (true) { if (sw_unlikely(tc.has_timedout(Socket::TIMEOUT_READ))) { - return false; + break; } retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { @@ -1480,10 +1494,11 @@ bool HttpClient::recv_http_response(double timeout) { socket->set_err(ECONNRESET); if (total_bytes > 0 && !swoole_http_should_keep_alive(&parser)) { http_parser_on_message_complete(&parser); - return true; + success = true; + break; } } - return false; + break; } if (!header_completed) { @@ -1493,7 +1508,7 @@ bool HttpClient::recv_http_response(double timeout) { if (buffer->length == buffer->size) { swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Http header too large"); socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL); - return false; + break; } header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0; continue; @@ -1508,11 +1523,15 @@ bool HttpClient::recv_http_response(double timeout) { total_bytes += retval; parsed_n = swoole_http_parser_execute(&parser, &http_parser_settings, buffer->str, retval); swoole_trace_log(SW_TRACE_HTTP_CLIENT, - "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", - parsed_n, - retval, - total_bytes, - parser.state == s_start_res); + "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", + parsed_n, + retval, + total_bytes, + parser.state == s_start_res); + if (socket->get_socket()->close_wait) { + success = false; + break; + } if (parser.state == s_start_res) { // handle redundant data (websocket packet) if (parser.upgrade && (size_t) retval > parsed_n + SW_WEBSOCKET_HEADER_LEN) { @@ -1520,41 +1539,92 @@ bool HttpClient::recv_http_response(double timeout) { buffer->offset = parsed_n; buffer->reduce(parsed_n); } - return true; + success = true; + break; } if (sw_unlikely(parser.state == s_dead)) { socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL); - return false; + break; } } + if (!success) { + php_swoole_socket_set_error_properties(zobject, socket); + zend_update_property_long(swoole_http_client_coro_ce, + SW_Z8_OBJ_P(zobject), + ZEND_STRL("statusCode"), + socket->errCode == ETIMEDOUT ? ESTATUS_REQUEST_TIMEOUT : ESTATUS_SERVER_RESET); + close(); + return false; + } + /** + * TODO: Sec-WebSocket-Accept check + */ + if (websocket) { + socket->open_length_check = 1; + socket->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN; + socket->protocol.package_length_offset = 0; + socket->protocol.package_body_offset = 0; + socket->protocol.get_package_length = websocket::get_package_length; + } + // handler keep alive + if (!websocket && (!keep_alive || connection_close)) { + close(); + } else { + reset(); + } + + return true; } -bool HttpClient::upgrade(std::string path) { - defer = false; - if (!websocket) { - char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; - zval *zheaders = sw_zend_read_and_convert_property_array( - swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); - create_token(SW_WEBSOCKET_KEY_LENGTH, buf); - add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); - add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); - add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); - add_assoc_str_ex(zheaders, - ZEND_STRL("Sec-WebSocket-Key"), - php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); -#ifdef SW_HAVE_ZLIB - if (websocket_compression) { - add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); +bool Client::recv_websocket_frame(zval *zframe, double timeout) { + SW_ASSERT(websocket); + ZVAL_FALSE(zframe); + + ssize_t retval = socket->recv_packet(timeout); + if (retval <= 0) { + php_swoole_socket_set_error_properties(zobject, socket); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); + if (socket->errCode != ETIMEDOUT) { + close(); } + return false; + } else { + String msg; + msg.length = retval; + msg.str = socket->get_read_buffer()->str; +#ifdef SW_HAVE_ZLIB + php_swoole_websocket_frame_unpack_ex(&msg, zframe, accept_websocket_compression); +#else + php_swoole_websocket_frame_unpack(&msg, zframe); #endif - exec(path); + zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("fd"), socket->get_fd()); + return true; } - return websocket; } -bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { +bool Client::upgrade(std::string path) { + defer = false; + char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); + create_token(SW_WEBSOCKET_KEY_LENGTH, buf); + add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); + add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); + add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); + add_assoc_str_ex(zheaders, + ZEND_STRL("Sec-WebSocket-Key"), + php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); +#ifdef SW_HAVE_ZLIB + if (websocket_compression) { + add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); + } +#endif + return exec(path); +} + +bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) { if (!websocket) { swoole_set_last_error(SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); php_swoole_fatal_error(E_WARNING, "websocket handshake failed, cannot push data"); @@ -1564,46 +1634,27 @@ bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "websocket handshake failed, cannot push data"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - return false; - } - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_CONNECT_FAILED); return false; } - String *buffer = socket->get_write_buffer(); buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, websocket_compression) < 0) { + if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, accept_websocket_compression) < 0) { return false; } } else { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags, websocket_mask, websocket_compression) < 0) { + if (php_swoole_websocket_frame_pack( + buffer, zdata, opcode, flags, websocket_mask, accept_websocket_compression) < 0) { return false; } } if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { + php_swoole_socket_set_error_properties(zobject, socket); zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); close(); return false; } else { @@ -1611,8 +1662,8 @@ bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { } } -void HttpClient::reset() { - wait = false; +void Client::reset() { + wait_response = false; #ifdef SW_HAVE_COMPRESSION compress_method = HTTP_COMPRESS_NONE; compression_error = false; @@ -1628,13 +1679,18 @@ void HttpClient::reset() { BrotliDecoderDestroyInstance(brotli_decoder_state); brotli_decoder_state = nullptr; } +#endif +#ifdef SW_HAVE_ZSTD + if (zstd_stream) { + ZSTD_freeDStream(zstd_stream); + zstd_stream = nullptr; + } #endif if (has_upload_files) { zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("uploadFiles")); } if (download_file != nullptr) { - delete download_file; - download_file = nullptr; + download_file.reset(); download_file_name.release(); download_offset = 0; zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("downloadFile")); @@ -1642,32 +1698,44 @@ void HttpClient::reset() { } } -bool HttpClient::close(const bool should_be_reset) { +void Client::socket_dtor() { + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + zend_update_property_null(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + if (tmp_write_buffer) { + delete tmp_write_buffer; + } + tmp_write_buffer = socket->pop_write_buffer(); + socket = nullptr; + zval_ptr_dtor(&zsocket); + ZVAL_NULL(&zsocket); +} + +/** + * The socket member variables cannot be read after Socket::close(), + * MUST return to the php layer, otherwise a memory error will occur. + * The client, mysql client, http2 client also need to follow this coding convention. + */ +bool Client::close(const bool should_be_reset) { Socket *_socket = socket; - if (_socket) { - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!_socket->has_bound()) { - if (should_be_reset) { - reset(); - } - // reset the properties that depend on the connection - websocket = false; -#ifdef SW_HAVE_ZLIB - websocket_compression = false; -#endif - if (tmp_write_buffer) { - delete tmp_write_buffer; - } - tmp_write_buffer = socket->pop_write_buffer(); - socket = nullptr; - } - php_swoole_client_coro_socket_free(_socket); + if (!_socket) { + return false; + } + if (in_callback) { + _socket->get_socket()->close_wait = 1; return true; } - return false; + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + if (!_socket->close()) { + php_swoole_socket_set_error_properties(zobject, _socket); + return false; + } + if (should_be_reset) { + reset(); + } + return true; } -HttpClient::~HttpClient() { +Client::~Client() { close(); if (body) { delete body; @@ -1675,30 +1743,33 @@ HttpClient::~HttpClient() { if (tmp_write_buffer) { delete tmp_write_buffer; } + if (write_func) { + delete write_func; + } } -static sw_inline HttpClientObject *php_swoole_http_client_coro_fetch_object(zend_object *obj) { +static sw_inline HttpClientObject *http_client_coro_fetch_object(zend_object *obj) { return (HttpClientObject *) ((char *) obj - swoole_http_client_coro_handlers.offset); } -static sw_inline HttpClient *php_swoole_get_phc(zval *zobject) { - HttpClient *phc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(zobject))->phc; +static sw_inline Client *http_client_coro_get_client(zval *zobject) { + Client *phc = http_client_coro_fetch_object(Z_OBJ_P(zobject))->client; if (UNEXPECTED(!phc)) { - php_swoole_fatal_error(E_ERROR, "you must call Http Client constructor first"); + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return phc; } -static void php_swoole_http_client_coro_free_object(zend_object *object) { - HttpClientObject *hcc = php_swoole_http_client_coro_fetch_object(object); - if (hcc->phc) { - delete hcc->phc; - hcc->phc = nullptr; +static void http_client_coro_free_object(zend_object *object) { + HttpClientObject *hcc = http_client_coro_fetch_object(object); + if (hcc->client) { + delete hcc->client; + hcc->client = nullptr; } zend_object_std_dtor(&hcc->std); } -static zend_object *php_swoole_http_client_coro_create_object(zend_class_entry *ce) { +static zend_object *http_client_coro_create_object(zend_class_entry *ce) { HttpClientObject *hcc = (HttpClientObject *) zend_object_alloc(sizeof(HttpClientObject), ce); zend_object_std_init(&hcc->std, ce); object_properties_init(&hcc->std, ce); @@ -1709,17 +1780,22 @@ static zend_object *php_swoole_http_client_coro_create_object(zend_class_entry * void php_swoole_http_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_http_client_coro, "Swoole\\Coroutine\\Http\\Client", - nullptr, "Co\\Http\\Client", swoole_http_client_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_client_coro); SW_SET_CLASS_CLONEABLE(swoole_http_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_client_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_client_coro, - php_swoole_http_client_coro_create_object, - php_swoole_http_client_coro_free_object, - HttpClientObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_http_client_coro, http_client_coro_create_object, http_client_coro_free_object, HttpClientObject, std); +#if PHP_VERSION_ID >= 80200 + zend_add_parameter_attribute( + (zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setbasicauth")), + 1, + ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), + 0); +#endif + + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); // client status zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); @@ -1750,23 +1826,20 @@ void php_swoole_http_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_http_client_coro_exception, "Swoole\\Coroutine\\Http\\Client\\Exception", - nullptr, "Co\\Http\\Client\\Exception", nullptr, swoole_exception); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED", HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT", HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET", HTTP_CLIENT_ESTATUS_SERVER_RESET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED", HTTP_CLIENT_ESTATUS_SEND_FAILED); - -#ifdef SW_HAVE_COMPRESSION - swoole_zlib_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); -#endif + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED", + swoole::coroutine::http::ESTATUS_CONNECT_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT", + swoole::coroutine::http::ESTATUS_REQUEST_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET", swoole::coroutine::http::ESTATUS_SERVER_RESET); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED", swoole::coroutine::http::ESTATUS_SEND_FAILED); } static PHP_METHOD(swoole_http_client_coro, __construct) { - HttpClientObject *hcc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + HttpClientObject *hcc = http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); char *host; size_t host_len; zend_long port = 0; @@ -1797,16 +1870,13 @@ static PHP_METHOD(swoole_http_client_coro, __construct) { RETURN_FALSE; } #endif - if (port == 0) { - port = ssl ? 443 : 80; - } - hcc->phc = new HttpClient(ZEND_THIS, std::string(host, host_len), port, ssl); + hcc->client = new Client(ZEND_THIS, std::string(host, host_len), port, ssl); } static PHP_METHOD(swoole_http_client_coro, __destruct) {} static PHP_METHOD(swoole_http_client_coro, set) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); zval *zset; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1825,13 +1895,13 @@ static PHP_METHOD(swoole_http_client_coro, set) { } static PHP_METHOD(swoole_http_client_coro, getDefer) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); RETURN_BOOL(phc->defer); } static PHP_METHOD(swoole_http_client_coro, setDefer) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); zend_bool defer = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1872,7 +1942,7 @@ static PHP_METHOD(swoole_http_client_coro, setHeaders) { } static PHP_METHOD(swoole_http_client_coro, setBasicAuth) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *username, *password; size_t username_len, password_len; @@ -2026,7 +2096,7 @@ static PHP_METHOD(swoole_http_client_coro, addData) { } static PHP_METHOD(swoole_http_client_coro, execute) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -2038,7 +2108,7 @@ static PHP_METHOD(swoole_http_client_coro, execute) { } static PHP_METHOD(swoole_http_client_coro, get) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -2052,7 +2122,7 @@ static PHP_METHOD(swoole_http_client_coro, get) { } static PHP_METHOD(swoole_http_client_coro, post) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; zval *post_data; @@ -2069,7 +2139,7 @@ static PHP_METHOD(swoole_http_client_coro, post) { } static PHP_METHOD(swoole_http_client_coro, download) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path; size_t path_len; zval *download_file; @@ -2089,7 +2159,7 @@ static PHP_METHOD(swoole_http_client_coro, download) { } static PHP_METHOD(swoole_http_client_coro, upgrade) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -2101,7 +2171,11 @@ static PHP_METHOD(swoole_http_client_coro, upgrade) { } static PHP_METHOD(swoole_http_client_coro, push) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + zval *zdata; zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; @@ -2117,12 +2191,16 @@ static PHP_METHOD(swoole_http_client_coro, push) { if (zflags != nullptr) { flags = zval_get_long(zflags); } - + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); RETURN_BOOL(phc->push(zdata, opcode, flags & WebSocket::FLAGS_ALL)); } static PHP_METHOD(swoole_http_client_coro, recv) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2130,17 +2208,18 @@ static PHP_METHOD(swoole_http_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (phc->websocket) { - phc->recv(return_value, timeout); - return; - } else { - RETURN_BOOL(phc->recv(timeout)); + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); + + if (!phc->websocket) { + RETURN_BOOL(phc->recv_response(timeout)); + } else if (!phc->recv_websocket_frame(return_value, timeout)) { + RETURN_FALSE; } } static PHP_METHOD(swoole_http_client_coro, close) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); - + Client *phc = http_client_coro_get_client(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); RETURN_BOOL(phc->close()); } @@ -2161,23 +2240,23 @@ static PHP_METHOD(swoole_http_client_coro, getStatusCode) { } static PHP_METHOD(swoole_http_client_coro, getHeaderOut) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->get_header_out(return_value); } static PHP_METHOD(swoole_http_client_coro, getsockname) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getsockname(return_value); } static PHP_METHOD(swoole_http_client_coro, getpeername) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getpeername(return_value); } #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_http_client_coro, getPeerCert) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getpeercert(return_value); } #endif diff --git a/ext-src/swoole_http_cookie.cc b/ext-src/swoole_http_cookie.cc new file mode 100644 index 00000000000..b1bdd88f44e --- /dev/null +++ b/ext-src/swoole_http_cookie.cc @@ -0,0 +1,440 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "php_swoole_http_server.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_cookie_arginfo.h" +END_EXTERN_C() + +using HttpCookie = swoole::http::Cookie; + +#define ILLEGAL_COOKIE_CHARACTER_PRINT "\",\", \";\", \" \", \"\\t\", \"\\r\", \"\\n\", \"\\013\", or \"\\014\"" +#define ILLEGAL_COOKIE_CHARACTER ",; \t\r\n\013\014" + +static const zend_long maxValidSeconds = 253402300800; + +zend_class_entry *swoole_http_cookie_ce; +static zend_object_handlers swoole_http_cookie_handlers; + +struct HttpCookieObject { + HttpCookie *cookie; + zend_object std; +}; + +static sw_inline HttpCookieObject *php_swoole_http_cookie_fetch_object(zend_object *obj) { + return (HttpCookieObject *) ((char *) obj - swoole_http_cookie_handlers.offset); +} + +static HttpCookie *php_swoole_http_get_cookie(zval *zobject) { + return php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie; +} + +HttpCookie *php_swoole_http_get_cooke_safety(zval *zobject) { + HttpCookie *cookie = php_swoole_http_get_cookie(zobject); + if (!cookie) { + swoole_set_last_error(SW_ERROR_HTTP_COOKIE_UNAVAILABLE); + return nullptr; + } + return cookie; +} + +void php_swoole_http_response_set_cookie(zval *zobject, HttpCookie *cookie) { + php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie = cookie; +} + +static zend_object *php_swoole_http_cookie_create_object(zend_class_entry *ce) { + HttpCookieObject *httpCookieObject = (HttpCookieObject *) zend_object_alloc(sizeof(HttpCookieObject), ce); + zend_object_std_init(&httpCookieObject->std, ce); + object_properties_init(&httpCookieObject->std, ce); + httpCookieObject->std.handlers = &swoole_http_cookie_handlers; + return &httpCookieObject->std; +} + +static void php_swoole_http_cookie_free_object(zend_object *object) { + HttpCookieObject *httpCookieObject = php_swoole_http_cookie_fetch_object(object); + delete httpCookieObject->cookie; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_cookie, __construct); +static PHP_METHOD(swoole_http_cookie, withName); +static PHP_METHOD(swoole_http_cookie, withValue); +static PHP_METHOD(swoole_http_cookie, withExpires); +static PHP_METHOD(swoole_http_cookie, withPath); +static PHP_METHOD(swoole_http_cookie, withDomain); +static PHP_METHOD(swoole_http_cookie, withSecure); +static PHP_METHOD(swoole_http_cookie, withHttpOnly); +static PHP_METHOD(swoole_http_cookie, withSameSite); +static PHP_METHOD(swoole_http_cookie, withPriority); +static PHP_METHOD(swoole_http_cookie, withPartitioned); +static PHP_METHOD(swoole_http_cookie, toArray); +static PHP_METHOD(swoole_http_cookie, toString); +static PHP_METHOD(swoole_http_cookie, reset); +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_http_cookie_methods[] = +{ + PHP_ME(swoole_http_cookie, __construct, arginfo_class_Swoole_Http_Cookie___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withName, arginfo_class_Swoole_Http_Cookie_withName, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withValue, arginfo_class_Swoole_Http_Cookie_withValue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withExpires, arginfo_class_Swoole_Http_Cookie_withExpires, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPath, arginfo_class_Swoole_Http_Cookie_withPath, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withDomain, arginfo_class_Swoole_Http_Cookie_withDomain, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withSecure, arginfo_class_Swoole_Http_Cookie_withSecure, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withHttpOnly, arginfo_class_Swoole_Http_Cookie_withHttpOnly, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withSameSite, arginfo_class_Swoole_Http_Cookie_withSameSite, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPriority, arginfo_class_Swoole_Http_Cookie_withPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPartitioned, arginfo_class_Swoole_Http_Cookie_withPartitioned, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, toString, arginfo_class_Swoole_Http_Cookie_toString, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, toArray, arginfo_class_Swoole_Http_Cookie_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, reset, arginfo_class_Swoole_Http_Cookie_reset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_http_cookie_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_http_cookie, "Swoole\\Http\\Cookie", nullptr, swoole_http_cookie_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_cookie); + SW_SET_CLASS_CLONEABLE(swoole_http_cookie, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_cookie, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_cookie, + php_swoole_http_cookie_create_object, + php_swoole_http_cookie_free_object, + HttpCookieObject, + std); +} + +#define HTTP_COOKIE_WITH_STR(field) \ + if (field) { \ + zend_string_release(field); \ + } \ + if (_##field && ZSTR_LEN(_##field) > 0) { \ + zend_string_addref(_##field); \ + field = _##field; \ + } else { \ + field = nullptr; \ + } \ + return this; + +HttpCookie *HttpCookie::withName(zend_string *_name) { + HTTP_COOKIE_WITH_STR(name); +} + +HttpCookie *HttpCookie::withValue(zend_string *_value) { + HTTP_COOKIE_WITH_STR(value); +} + +HttpCookie *HttpCookie::withDomain(zend_string *_domain) { + HTTP_COOKIE_WITH_STR(domain); +} + +HttpCookie *HttpCookie::withPath(zend_string *_path) { + HTTP_COOKIE_WITH_STR(path); +} + +HttpCookie *HttpCookie::withSameSite(zend_string *_sameSite) { + HTTP_COOKIE_WITH_STR(sameSite); +} + +HttpCookie *HttpCookie::withPriority(zend_string *_priority) { + HTTP_COOKIE_WITH_STR(priority); +} + +HttpCookie *HttpCookie::withExpires(zend_long _expires) { + expires = _expires; + return this; +} + +HttpCookie *HttpCookie::withSecure(zend_bool _secure) { + secure = _secure; + return this; +} + +HttpCookie *HttpCookie::withHttpOnly(zend_bool _httpOnly) { + httpOnly = _httpOnly; + return this; +} + +HttpCookie *HttpCookie::withPartitioned(zend_bool _partitioned) { + partitioned = _partitioned; + return this; +} + +zend_string *HttpCookie::toString() { + zend_string *date = nullptr; + if (name == nullptr || ZSTR_LEN(name) == 0) { + php_swoole_error(E_WARNING, "The name cannot be empty"); + return nullptr; + } + + if (strpbrk(ZSTR_VAL(name), "=" ILLEGAL_COOKIE_CHARACTER) != nullptr) { + php_swoole_error(E_WARNING, "The name cannot contain \"=\", " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + + smart_str_append(&buffer_, name); + + if (!value) { + smart_str_appends(&buffer_, "=deleted; expires="); + + date = php_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); + smart_str_append(&buffer_, date); + smart_str_appends(&buffer_, "; Max-Age=0"); + zend_string_free(date); + } else { + if (!encode_ && strpbrk(ZSTR_VAL(value), ILLEGAL_COOKIE_CHARACTER) != nullptr) { + php_swoole_error(E_WARNING, "The value cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + + smart_str_appendc(&buffer_, '='); + + if (encode_) { + zend_string *encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_append(&buffer_, encoded_value); + zend_string_free(encoded_value); + } else { + smart_str_append(&buffer_, value); + } + + if (expires > 0) { + if (expires >= maxValidSeconds) { + php_swoole_error(E_WARNING, "The expires cannot have a year greater than 9999"); + return nullptr; + } + smart_str_appends(&buffer_, "; expires="); + date = php_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), expires, 0); + smart_str_append(&buffer_, date); + smart_str_appends(&buffer_, "; Max-Age="); + + double diff = difftime(expires, php_time()); + smart_str_append_long(&buffer_, (zend_long) (diff >= 0 ? diff : 0)); + zend_string_free(date); + } + + if (path && ZSTR_LEN(path) > 0) { + if (strpbrk(ZSTR_VAL(path), ILLEGAL_COOKIE_CHARACTER) != NULL) { + php_swoole_error(E_WARNING, "The path option cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + smart_str_appends(&buffer_, "; path="); + smart_str_append(&buffer_, path); + } + + if (domain && ZSTR_LEN(domain) > 0) { + if (strpbrk(ZSTR_VAL(domain), ILLEGAL_COOKIE_CHARACTER) != NULL) { + php_swoole_error(E_WARNING, "The domain option cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + smart_str_appends(&buffer_, "; domain="); + smart_str_append(&buffer_, domain); + } + + if (secure) { + smart_str_appends(&buffer_, "; secure"); + } + + if (httpOnly) { + smart_str_appends(&buffer_, "; HttpOnly"); + } + + if (sameSite && ZSTR_LEN(sameSite) > 0) { + smart_str_appends(&buffer_, "; SameSite="); + smart_str_append(&buffer_, sameSite); + } + + if (priority && ZSTR_LEN(priority) > 0) { + smart_str_appends(&buffer_, "; Priority="); + smart_str_append(&buffer_, priority); + } + + if (partitioned) { + smart_str_appends(&buffer_, "; Partitioned"); + } + } + + return smart_str_extract(&buffer_); +} + +void HttpCookie::reset() { + expires = 0; + secure = false; + httpOnly = false; + partitioned = false; + encode_ = true; + + if (name) { + zend_string_release(name); + name = nullptr; + } + + if (value) { + zend_string_release(value); + value = nullptr; + } + + if (path) { + zend_string_release(path); + path = nullptr; + } + + if (domain) { + zend_string_release(domain); + domain = nullptr; + } + + if (sameSite) { + zend_string_release(sameSite); + sameSite = nullptr; + } + + if (priority) { + zend_string_release(priority); + priority = nullptr; + } + + smart_str_free_ex(&buffer_, false); +} + +#define HTTP_COOKIE_ADD_STR_TO_ARRAY(field) \ + if (field) { \ + add_assoc_str(return_value, #field, field); \ + } else { \ + add_assoc_string(return_value, #field, ""); \ + } + +void HttpCookie::toArray(zval *return_value) { + array_init(return_value); + + HTTP_COOKIE_ADD_STR_TO_ARRAY(name); + HTTP_COOKIE_ADD_STR_TO_ARRAY(value); + HTTP_COOKIE_ADD_STR_TO_ARRAY(path); + HTTP_COOKIE_ADD_STR_TO_ARRAY(domain); + HTTP_COOKIE_ADD_STR_TO_ARRAY(sameSite); + HTTP_COOKIE_ADD_STR_TO_ARRAY(priority); + + add_assoc_bool(return_value, "encode", encode_); + add_assoc_long(return_value, "expires", expires); + add_assoc_bool(return_value, "secure", secure); + add_assoc_bool(return_value, "httpOnly", httpOnly); + add_assoc_bool(return_value, "partitioned", partitioned); +} + +HttpCookie::~Cookie() { + reset(); +} + +static PHP_METHOD(swoole_http_cookie, __construct) { + zend_bool encode = true; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(encode) + ZEND_PARSE_PARAMETERS_END(); + + php_swoole_http_response_set_cookie(ZEND_THIS, new HttpCookie(encode)); +} + +#define PHP_METHOD_HTTP_COOKIE_WITH_STR(field) \ + zend_string *field; \ + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); \ + \ + ZEND_PARSE_PARAMETERS_START(1, 1) \ + Z_PARAM_STR(field) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + cookie->with##field(field); \ + RETURN_ZVAL(ZEND_THIS, 1, 0); + +#define PHP_METHOD_HTTP_COOKIE_WITH_BOOL(field) \ + zend_bool field = false; \ + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); \ + \ + ZEND_PARSE_PARAMETERS_START(0, 1) \ + Z_PARAM_OPTIONAL \ + Z_PARAM_BOOL(field) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + cookie->with##field(field); \ + RETURN_ZVAL(ZEND_THIS, 1, 0); + +static PHP_METHOD(swoole_http_cookie, withName) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Name); +} + +static PHP_METHOD(swoole_http_cookie, withValue) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Value); +} + +static PHP_METHOD(swoole_http_cookie, withExpires) { + zend_long expires = 0; + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expires) + ZEND_PARSE_PARAMETERS_END(); + + cookie->withExpires(expires); + RETURN_ZVAL(ZEND_THIS, 1, 0); +} + +static PHP_METHOD(swoole_http_cookie, withPath) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Path); +} + +static PHP_METHOD(swoole_http_cookie, withDomain) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Domain); +} + +static PHP_METHOD(swoole_http_cookie, withSecure) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Secure); +} + +static PHP_METHOD(swoole_http_cookie, withHttpOnly) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(HttpOnly); +} + +static PHP_METHOD(swoole_http_cookie, withSameSite) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(SameSite); +} + +static PHP_METHOD(swoole_http_cookie, withPriority) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Priority); +} + +static PHP_METHOD(swoole_http_cookie, withPartitioned) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Partitioned); +} + +static PHP_METHOD(swoole_http_cookie, toString) { + auto cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); + auto cookie_str = cookie->toString(); + if (!cookie_str) { + cookie->reset(); + RETURN_FALSE; + } + ZVAL_STR(return_value, cookie_str); +} + +static PHP_METHOD(swoole_http_cookie, toArray) { + php_swoole_http_get_cooke_safety(ZEND_THIS)->toArray(return_value); +} + +static PHP_METHOD(swoole_http_cookie, reset) { + php_swoole_http_get_cooke_safety(ZEND_THIS)->reset(); +} diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 4c5c808c349..184cb9f4878 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,18 +18,10 @@ SW_EXTERN_C_BEGIN #include "ext/standard/url.h" +#include "stubs/php_swoole_http_request_arginfo.h" +#include "thirdparty/php/main/SAPI.h" SW_EXTERN_C_END -#include "main/php_variables.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif - enum http_upload_errno { HTTP_UPLOAD_ERR_OK = 0, HTTP_UPLOAD_ERR_INI_SIZE, @@ -38,6 +30,7 @@ enum http_upload_errno { HTTP_UPLOAD_ERR_NO_FILE, HTTP_UPLOAD_ERR_NO_TMP_DIR = 6, HTTP_UPLOAD_ERR_CANT_WRITE, + HTTP_UPLOAD_ERR_FILE_READY, }; using HttpRequest = swoole::http::Request; @@ -46,6 +39,7 @@ using swoole::Connection; using swoole::ListenPort; using swoole::microtime; using swoole::Server; +using swoole::http_server::ParseCookieCallback; static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); @@ -68,8 +62,8 @@ static int http_request_on_path(swoole_http_parser *parser, const char *at, size return 0; } -static inline char *http_trim_double_quote(char *ptr, int *len) { - int i; +static inline char *http_trim_double_quote(char *ptr, size_t *len) { + size_t i; char *tmp = ptr; // ltrim('"') @@ -83,9 +77,9 @@ static inline char *http_trim_double_quote(char *ptr, int *len) { } } // rtrim('"') - for (i = (*len) - 1; i >= 0; i--) { - if (tmp[i] == '"') { - tmp[i] = 0; + for (i = (*len); i > 0; i--) { + if (tmp[i - 1] == '"') { + tmp[i - 1] = 0; (*len)--; continue; } else { @@ -95,10 +89,6 @@ static inline char *http_trim_double_quote(char *ptr, int *len) { return tmp; } -static sw_inline const char *http_get_method_name(enum swoole_http_method method) { - return swoole_http_method_str(method); -} - // clang-format off static const swoole_http_parser_settings http_parser_settings = { @@ -130,16 +120,37 @@ size_t HttpContext::parse(const char *data, size_t length) { return swoole_http_parser_execute(&parser, &http_parser_settings, data, length); } +bool HttpContext::parse_multipart_data(const char *at, size_t length) { + ssize_t n = multipart_parser_execute(mt_parser, at, length); + if (n < 0) { + int l_error = multipart_parser_error_msg(mt_parser, sw_tg_buffer()->str, sw_tg_buffer()->size); + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, reason: %.*s", + l_error, + sw_tg_buffer()->str); + return false; + } else if (n != (ssize_t) length) { + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %lu/%zu bytes processed", + n, + length); + return false; + } + return true; +} + zend_class_entry *swoole_http_request_ce; static zend_object_handlers swoole_http_request_handlers; -typedef struct { +struct HttpRequestObject { HttpContext *ctx; zend_object std; -} http_request_t; +}; -static sw_inline http_request_t *php_swoole_http_request_fetch_object(zend_object *obj) { - return (http_request_t *) ((char *) obj - swoole_http_request_handlers.offset); +static sw_inline HttpRequestObject *php_swoole_http_request_fetch_object(zend_object *obj) { + return (HttpRequestObject *) ((char *) obj - swoole_http_request_handlers.offset); } HttpContext *php_swoole_http_request_get_context(zval *zobject) { @@ -151,26 +162,24 @@ void php_swoole_http_request_set_context(zval *zobject, HttpContext *ctx) { } static void php_swoole_http_request_free_object(zend_object *object) { - http_request_t *request = php_swoole_http_request_fetch_object(object); + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); HttpContext *ctx = request->ctx; - zval zobject, *ztmpfiles; - - ZVAL_OBJ(&zobject, object); - ztmpfiles = sw_zend_read_property_ex(swoole_http_request_ce, &zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES), 0); - if (ZVAL_IS_ARRAY(ztmpfiles)) { - zval *z_file_path; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) { - if (Z_TYPE_P(z_file_path) != IS_STRING) { - continue; - } - unlink(Z_STRVAL_P(z_file_path)); - if (SG(rfc1867_uploaded_files)) { - zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path)); + + if (ctx) { + zval *ztmpfiles = ctx->request.ztmpfiles; + if (ztmpfiles && ZVAL_IS_ARRAY(ztmpfiles)) { + zval *z_file_path; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) { + if (Z_TYPE_P(z_file_path) != IS_STRING) { + continue; + } + unlink(Z_STRVAL_P(z_file_path)); + if (SG(rfc1867_uploaded_files)) { + zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path)); + } } + SW_HASHTABLE_FOREACH_END(); } - SW_HASHTABLE_FOREACH_END(); - } - if (ctx) { ctx->request.zobject = nullptr; ctx->free(); } @@ -179,7 +188,7 @@ static void php_swoole_http_request_free_object(zend_object *object) { } static zend_object *php_swoole_http_request_create_object(zend_class_entry *ce) { - http_request_t *request = (http_request_t *) zend_object_alloc(sizeof(http_request_t), ce); + HttpRequestObject *request = (HttpRequestObject *) zend_object_alloc(sizeof(HttpRequestObject), ce); zend_object_std_init(&request->std, ce); object_properties_init(&request->std, ce); request->std.handlers = &swoole_http_request_handlers; @@ -192,52 +201,36 @@ static PHP_METHOD(swoole_http_request, create); static PHP_METHOD(swoole_http_request, parse); static PHP_METHOD(swoole_http_request, isCompleted); static PHP_METHOD(swoole_http_request, getMethod); -static PHP_METHOD(swoole_http_request, rawContent); -static PHP_METHOD(swoole_http_request, __destruct); +static PHP_METHOD(swoole_http_request, getContent); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_create, 0, 0, 0) -ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_parse, 0, 0, 1) -ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_http_request_methods[] = { - PHP_ME(swoole_http_request, rawContent, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_request, getContent, rawContent, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, getData, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, create, arginfo_swoole_http_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_http_request, parse, arginfo_swoole_http_parse, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, isCompleted, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, getMethod, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, __destruct, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getContent, arginfo_class_Swoole_Http_Request_getContent, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_request, rawContent, getContent, arginfo_class_Swoole_Http_Request_getContent, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getData, arginfo_class_Swoole_Http_Request_getData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, create, arginfo_class_Swoole_Http_Request_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_http_request, parse, arginfo_class_Swoole_Http_Request_parse, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, isCompleted, arginfo_class_Swoole_Http_Request_isCompleted, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getMethod, arginfo_class_Swoole_Http_Request_getMethod, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_http_request_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_http_request, "Swoole\\Http\\Request", "swoole_http_request", nullptr, swoole_http_request_methods); + SW_INIT_CLASS_ENTRY(swoole_http_request, "Swoole\\Http\\Request", nullptr, swoole_http_request_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_request); SW_SET_CLASS_CLONEABLE(swoole_http_request, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_request, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_request, php_swoole_http_request_create_object, php_swoole_http_request_free_object, - http_request_t, + HttpRequestObject, std); zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); -#ifdef SW_USE_HTTP2 zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); -#endif zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("server"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); @@ -249,134 +242,108 @@ void php_swoole_http_request_minit(int module_number) { static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; - add_assoc_stringl_ex(ctx->request.zserver, ZEND_STRL("query_string"), (char *) at, length); + + zval tmp; + HashTable *ht = Z_ARR_P(ctx->request.zserver); + ZVAL_STRINGL(&tmp, (char *) at, length); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_QUERY_STRING), &tmp); + // parse url params - sapi_module.treat_data(PARSE_STRING, - estrndup(at, length), // it will be freed by treat_data - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, ZEND_STRL("get"))); + swoole_php_treat_data( + PARSE_STRING, + estrndup(at, length), // it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, SW_ZSTR_KNOWN(SW_ZEND_STR_GET))); return 0; } static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; - ctx->current_header_name = (char *) at; + ctx->current_header_name = at; ctx->current_header_name_len = length; return 0; } -bool HttpContext::parse_form_data(const char *boundary_str, int boundary_len) { +bool HttpContext::init_multipart_parser(const char *boundary_str, int boundary_len) { mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); if (!mt_parser) { php_swoole_fatal_error(E_WARNING, "multipart_parser_init() failed"); return false; } - + form_data_buffer = new String(SW_BUFFER_SIZE_STD); mt_parser->data = this; return true; } -void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool url_decode) { - char keybuf[SW_HTTP_COOKIE_KEYLEN]; - char valbuf[SW_HTTP_COOKIE_VALLEN]; - char *_c = (char *) at; - - char *_value; - int klen = 0; - int vlen = 0; - int state = -1; - - int i = 0, j = 0; - while (_c < at + length) { - if (state <= 0 && *_c == '=') { - klen = i - j + 1; - if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning("cookie[%.*s...] name length %d is exceed the max name len %d", - 8, - (char *) at + j, - klen, - SW_HTTP_COOKIE_KEYLEN); - return; - } - memcpy(keybuf, (char *) at + j, klen - 1); - keybuf[klen - 1] = 0; - - j = i + 1; - state = 1; - } else if (state == 1 && *_c == ';') { - vlen = i - j; - if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); - return; - } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - vlen = php_url_decode(_value, vlen); - if (klen > 1) { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); - } - j = i + 1; - state = -1; - } else if (state < 0) { - if (isspace(*_c)) { - // Remove leading spaces from cookie names - j++; - } else { - state = 0; - } - } - _c++; - i++; - } - if (j < (off_t) length) { - vlen = i - j; - if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning( - "cookie[%.*s...] name length %d is exceed the max name len %d", 8, keybuf, klen, SW_HTTP_COOKIE_KEYLEN); - return; - } - keybuf[klen - 1] = 0; - if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); - return; - } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - if (url_decode) { - vlen = php_url_decode(_value, vlen); - } - if (klen > 1) { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); +bool HttpContext::get_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { + if (!http_server::parse_multipart_boundary(at, length, offset, out_boundary_str, out_boundary_len)) { + swoole_warning("boundary of multipart/form-data not found, fd:%ld", fd); + /* make it same with protocol error */ + parser.state = s_dead; + return false; + } + return true; +} + +void swoole_http_parse_cookie(zval *zcookies, const char *at, size_t length) { + if (length == 0) { + return; + } + + swoole_php_treat_data(PARSE_COOKIE, estrndup(at, length), zcookies); +} + +static void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) { + zval *zfiles = swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES)); + add_next_index_stringl(zfiles, file, l_file); + // support is_upload_file + zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file); +} + +bool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value) { + if (0 == length) { + return false; + } + if (SW_STRCASEEQ(at, length, value)) { + return true; + } + + char *var; + const char *separator = ",\0"; + char *strtok_buf = nullptr; + size_t var_len; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + var = php_strtok_r(_c, separator, &strtok_buf); + while (var) { + var_len = swoole::ltrim(&var, strlen(var)); + var_len = swoole::rtrim(var, var_len); + if (swoole_strcaseeq(var, var_len, value, strlen(value))) { + return true; } + var = php_strtok_r(nullptr, separator, &strtok_buf); } + return false; } static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { - size_t offset = 0; HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; + const char *header_name = ctx->current_header_name; size_t header_len = ctx->current_header_name_len; - char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); - if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { + if (ctx->parse_cookie && SW_STRCASEEQ(header_name, header_len, "cookie")) { zval *zcookie = swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)); swoole_http_parse_cookie(zcookie, at, length); - efree(header_name); return 0; - } else if (SW_STREQ(header_name, header_len, "upgrade") && SW_STRCASEEQ(at, length, "websocket")) { + } else if (SW_STRCASEEQ(header_name, header_len, "upgrade") && + swoole_http_token_list_contains_value(at, length, "websocket")) { ctx->websocket = 1; if (ctx->co_socket) { goto _add_header; @@ -387,8 +354,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); - efree(header_name); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); return -1; } ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); @@ -397,54 +363,58 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } } else if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || parser->method == PHP_HTTP_PATCH) && - SW_STREQ(header_name, header_len, "content-type")) { - if (SW_STRCASECT(at, length, "application/x-www-form-urlencoded")) { + SW_STRCASEEQ(header_name, header_len, "content-type")) { + if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; - } else if (SW_STRCASECT(at, length, "multipart/form-data")) { - offset = sizeof("multipart/form-data") - 1; - // skip ' ' and ';' - while (offset < length && (at[offset] == ' ' || at[offset] == ';')) { - offset++; - } - // skip 'boundary=' - offset += sizeof("boundary=") - 1; - int boundary_len = length - offset; - char *boundary_str = (char *) at + offset; - // find eof of boundary - if (boundary_len > 0) { - // find ';' - char *tmp = (char *) memchr(boundary_str, ';', boundary_len); - if (tmp) { - boundary_len = tmp - boundary_str; - } - } - if (boundary_len <= 0) { - swoole_warning("invalid multipart/form-data body fd:%ld", ctx->fd); - /* make it same with protocol error */ - ctx->parser.state = s_dead; + } else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) { + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!ctx->get_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) { return -1; } - // trim '"' - if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { - boundary_str++; - boundary_len -= 2; - } swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); - ctx->parse_form_data(boundary_str, boundary_len); + ctx->init_multipart_parser(boundary_str, boundary_len); } } #ifdef SW_HAVE_COMPRESSION - else if (ctx->enable_compression && SW_STREQ(header_name, header_len, "accept-encoding")) { + else if (ctx->enable_compression && SW_STRCASEEQ(header_name, header_len, "accept-encoding")) { ctx->set_compression_method(at, length); } #endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) { + else if (SW_STRCASEEQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { ctx->recv_chunked = 1; } _add_header: - add_assoc_stringl_ex(zheader, header_name, header_len, (char *) at, length); - efree(header_name); + zval tmp; + ZVAL_STRINGL(&tmp, (char *) at, length); + + /** + * some common request header key + */ + if (SW_STRCASEEQ(header_name, header_len, "host")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "user-agent")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_USER_AGENT), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "accept")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "content-type")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_TYPE), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "content-length")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_LENGTH), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "authorization")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_AUTHORIZATION), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "connection")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONNECTION), &tmp); + } else if (SW_STRCASEEQ(header_name, header_len, "accept-encoding")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT_ENCODING), &tmp); + } else { + char *new_header_name = estrndup(header_name, header_len); + zend_str_tolower_copy(new_header_name, header_name, header_len); + zend_hash_str_update(Z_ARR_P(zheader), new_header_name, header_len, &tmp); + efree(new_header_name); + } return 0; } @@ -452,7 +422,6 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * static int http_request_on_headers_complete(swoole_http_parser *parser) { HttpContext *ctx = (HttpContext *) parser->data; const char *vpath = ctx->request.path, *end = vpath + ctx->request.path_len, *p = end; - zval *zserver = ctx->request.zserver; ctx->request.version = parser->http_major * 100 + parser->http_minor; ctx->request.ext = end; @@ -468,18 +437,24 @@ static int http_request_on_headers_complete(swoole_http_parser *parser) { } } - ctx->keepalive = swoole_http_should_keep_alive(parser); + HashTable *ht = Z_ARR_P(ctx->request.zserver); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_METHOD2), swoole_http_method_str(parser->method)); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_URI), ctx->request.path, ctx->request.path_len); - add_assoc_string(zserver, "request_method", http_get_method_name(parser->method)); - add_assoc_stringl_ex(zserver, ZEND_STRL("request_uri"), ctx->request.path, ctx->request.path_len); // path_info should be decoded zend_string *zstr_path = zend_string_init(ctx->request.path, ctx->request.path_len, 0); ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path)); - add_assoc_str_ex(zserver, ZEND_STRL("path_info"), zstr_path); - add_assoc_long_ex(zserver, ZEND_STRL("request_time"), time(nullptr)); - add_assoc_double_ex(zserver, ZEND_STRL("request_time_float"), microtime()); - add_assoc_string(zserver, "server_protocol", (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0")); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH_INFO), zstr_path); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (zend_long) time(nullptr)); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime()); + http_server_add_server_array( + ht, + SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL), + (ctx->request.version == 101 ? SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP11) : SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP10))); + + ctx->keepalive = swoole_http_should_keep_alive(parser); ctx->current_header_name = nullptr; return 0; @@ -492,7 +467,7 @@ static int multipart_body_on_header_field(multipart_parser *p, const char *at, s static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) { char value_buf[SW_HTTP_FORM_KEYLEN]; - int value_len; + size_t value_len; int ret = 0; HttpContext *ctx = (HttpContext *) p->data; @@ -510,27 +485,34 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s } size_t header_len = ctx->current_header_name_len; - char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); + zend::CharPtr _header_name; + _header_name.assign_tolower(ctx->current_header_name, header_len); + char *header_name = _header_name.get(); if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { - // not form data - if (swoole_strnpos(at, length, ZEND_STRL("form-data;")) < 0) { - goto _end; + size_t offset = 0; + if (swoole_strnpos(at, length, ZEND_STRL("form-data;")) >= 0) { + offset += sizeof("form-data;") - 1; + } else if (swoole_strnpos(at, length, ZEND_STRL("attachment;")) >= 0) { + offset += sizeof("attachment;") - 1; + } else { + swoole_warning("Unsupported Content-Disposition [%.*s]", (int) length, at); + return ret; } zval tmp_array; array_init(&tmp_array); - swoole_http_parse_cookie(&tmp_array, at + sizeof("form-data;") - 1, length - sizeof("form-data;") + 1, false); + swoole_http_parse_cookie(&tmp_array, at + offset, length - offset); zval *zform_name; if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) { - goto _end; + return ret; } if (Z_STRLEN_P(zform_name) >= SW_HTTP_FORM_KEYLEN) { swoole_warning("form_name[%s] is too large", Z_STRVAL_P(zform_name)); ret = -1; - goto _end; + return ret; } swoole_strlcpy(value_buf, Z_STRVAL_P(zform_name), sizeof(value_buf)); @@ -548,7 +530,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) { swoole_warning("filename[%s] is too large", Z_STRVAL_P(zfilename)); ret = -1; - goto _end; + return ret; } ctx->current_input_name = estrndup(tmp, value_len); ctx->current_input_name_len = value_len; @@ -556,10 +538,13 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s zval *z_multipart_header = sw_malloc_zval(); array_init(z_multipart_header); - add_assoc_string(z_multipart_header, "name", (char *) ""); - add_assoc_string(z_multipart_header, "type", (char *) ""); + if (ctx->tmp_content_type) { + add_assoc_stringl(z_multipart_header, "type", ctx->tmp_content_type, ctx->tmp_content_type_len); + ctx->tmp_content_type = nullptr; + } else { + add_assoc_string(z_multipart_header, "type", (char *) ""); + } add_assoc_string(z_multipart_header, "tmp_name", (char *) ""); - add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); add_assoc_long(z_multipart_header, "size", 0); swoole_strlcpy(value_buf, Z_STRVAL_P(zfilename), sizeof(value_buf)); @@ -569,40 +554,49 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s add_assoc_stringl(z_multipart_header, "name", tmp, value_len); if (value_len == 0) { add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_NO_FILE); + } else { + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); } - ctx->current_multipart_header = z_multipart_header; } zval_ptr_dtor(&tmp_array); - } else if (SW_STRCASEEQ(header_name, header_len, "content-type") && ctx->current_multipart_header) { - zval *z_multipart_header = ctx->current_multipart_header; - zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); - if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) { - add_assoc_stringl(z_multipart_header, "type", (char *) at, length); + } else if (SW_STRCASEEQ(header_name, header_len, "content-type")) { + if (ctx->current_multipart_header) { + zval *z_multipart_header = ctx->current_multipart_header; + zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); + if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) { + add_assoc_stringl(z_multipart_header, "type", (char *) at, length); + } + } else { + ctx->tmp_content_type = at; + ctx->tmp_content_type_len = length; } + } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) { + zval *z_multipart_header = ctx->current_multipart_header; + std::string tmp_file(at, length); + add_assoc_stringl(z_multipart_header, "tmp_name", at, length); + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_FILE_READY); + add_assoc_long(z_multipart_header, "size", swoole::file_get_size(tmp_file.c_str())); + http_request_add_upload_file(ctx, tmp_file.c_str(), tmp_file.length()); } - -_end: - efree(header_name); - return ret; } static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) p->data; if (ctx->current_form_data_name) { - swoole_http_form_data_buffer->append(at, length); + ctx->form_data_buffer->append(at, length); return 0; } if (p->fp == nullptr) { return 0; } - int n = fwrite(at, sizeof(char), length, (FILE *) p->fp); + ssize_t n = fwrite(at, sizeof(char), length, p->fp); if (n != (off_t) length) { zval *z_multipart_header = ctx->current_multipart_header; add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_CANT_WRITE); - fclose((FILE *) p->fp); + fclose(p->fp); p->fp = nullptr; swoole_sys_warning("write upload file failed"); @@ -657,14 +651,7 @@ static int multipart_body_on_header_complete(multipart_parser *p) { p->fp = fp; add_assoc_string(z_multipart_header, "tmp_name", file_path); - size_t file_path_len = strlen(file_path); - add_next_index_stringl( - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, ZEND_STRL("tmpfiles")), - file_path, - file_path_len); - // support is_upload_file - zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file_path, file_path_len, (char *) file_path); + http_request_add_upload_file(ctx, file_path, strlen(file_path)); return 0; } @@ -675,15 +662,15 @@ static int multipart_body_on_data_end(multipart_parser *p) { if (ctx->current_form_data_name) { php_register_variable_safe( ctx->current_form_data_name, - swoole_http_form_data_buffer->str, - swoole_http_form_data_buffer->length, + ctx->form_data_buffer->str, + ctx->form_data_buffer->length, swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); efree(ctx->current_form_data_name); ctx->current_form_data_name = nullptr; ctx->current_form_data_name_len = 0; - swoole_http_form_data_buffer->clear(); + ctx->form_data_buffer->clear(); return 0; } @@ -693,15 +680,23 @@ static int multipart_body_on_data_end(multipart_parser *p) { zval *z_multipart_header = ctx->current_multipart_header; if (p->fp != nullptr) { - long size = swoole::file_get_size((FILE *) p->fp); + long size = swoole::file_get_size(p->fp); add_assoc_long(z_multipart_header, "size", size); - fclose((FILE *) p->fp); + fclose(p->fp); p->fp = nullptr; } + zval *zerr; + if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")))) { + return 0; + } + if (zval_get_long(zerr) == HTTP_UPLOAD_ERR_FILE_READY) { + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); + } + zval *zfiles = swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, ZEND_STRL("files")); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_FILES)); int input_path_pos = swoole_strnpos(ctx->current_input_name, ctx->current_input_name_len, ZEND_STRL("[")); if (ctx->parse_files && input_path_pos > 0) { @@ -759,17 +754,11 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size } ctx->request.chunked_body->append(at, length); } else { + ctx->request.body_at = at - ctx->request.body_length; ctx->request.body_length += length; } - if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded) { - sapi_module.treat_data( - PARSE_STRING, - estrndup(at, length), // do not free, it will be freed by treat_data - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); - } else if (ctx->mt_parser != nullptr) { - multipart_parser *multipart_parser = ctx->mt_parser; + if (ctx->mt_parser != nullptr) { if (is_beginning) { /* Compatibility: some clients may send extra EOL */ do { @@ -780,9 +769,8 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size length--; } while (length != 0); } - size_t n = multipart_parser_execute(multipart_parser, at, length); - if (n != length) { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, n=%zu", n); + if (!ctx->parse_multipart_data(at, length)) { + return -1; } } @@ -795,16 +783,27 @@ static int http_request_message_complete(swoole_http_parser *parser) { if (ctx->request.chunked_body != nullptr && ctx->parse_body && ctx->request.post_form_urlencoded) { /* parse dechunked content */ - sapi_module.treat_data( + swoole_php_treat_data( PARSE_STRING, estrndup(ctx->request.chunked_body->str, content_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); + } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) { + swoole_php_treat_data( + PARSE_STRING, + estrndup(ctx->request.body_at, ctx->request.body_length), // do not free, it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); } if (ctx->mt_parser) { multipart_parser_free(ctx->mt_parser); ctx->mt_parser = nullptr; } + if (ctx->form_data_buffer) { + delete ctx->form_data_buffer; + ctx->form_data_buffer = nullptr; + } + ctx->completed = 1; swoole_trace_log(SW_TRACE_HTTP, "request body length=%ld", content_length); @@ -826,6 +825,11 @@ void HttpContext::set_compression_method(const char *accept_encoding, size_t len } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) { accept_compression = 1; compression_method = HTTP_COMPRESS_DEFLATE; +#ifdef SW_HAVE_ZSTD + } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("zstd")) >= 0) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_ZSTD; +#endif } else { accept_compression = 0; } @@ -841,6 +845,11 @@ const char *HttpContext::get_content_encoding() { else if (compression_method == HTTP_COMPRESS_BR) { return "br"; } +#endif +#ifdef SW_HAVE_ZSTD + else if (compression_method == HTTP_COMPRESS_ZSTD) { + return "zstd"; + } #endif else { return nullptr; @@ -848,7 +857,7 @@ const char *HttpContext::get_content_encoding() { } #endif -static PHP_METHOD(swoole_http_request, rawContent) { +static PHP_METHOD(swoole_http_request, getContent) { HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; @@ -860,12 +869,9 @@ static PHP_METHOD(swoole_http_request, rawContent) { RETURN_STRINGL(Z_STRVAL_P(zdata) + Z_STRLEN_P(zdata) - req->body_length, req->body_length); } else if (req->chunked_body && req->chunked_body->length != 0) { RETURN_STRINGL(req->chunked_body->str, req->chunked_body->length); - } -#ifdef SW_USE_HTTP2 - else if (req->h2_data_buffer && req->h2_data_buffer->length != 0) { + } else if (req->h2_data_buffer && req->h2_data_buffer->length != 0) { RETURN_STRINGL(req->h2_data_buffer->str, req->h2_data_buffer->length); } -#endif RETURN_EMPTY_STRING(); } @@ -876,12 +882,10 @@ static PHP_METHOD(swoole_http_request, getData) { RETURN_FALSE; } -#ifdef SW_USE_HTTP2 if (ctx->http2) { php_swoole_fatal_error(E_WARNING, "unable to get data from HTTP2 request"); RETURN_FALSE; } -#endif if (Z_TYPE(ctx->request.zdata) == IS_STRING) { RETURN_ZVAL(&ctx->request.zdata, 1, 0); @@ -992,8 +996,13 @@ static PHP_METHOD(swoole_http_request, getMethod) { if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - const char *method = http_get_method_name((ctx->parser).method); - RETURN_STRING(method); + if (ctx->http2) { + zval *zmethod = zend_hash_str_find(Z_ARR_P(ctx->request.zserver), ZEND_STRL("request_method")); + RETURN_ZVAL(zmethod, 1, 0); + } else { + const char *method = swoole_http_method_str((ctx->parser).method); + RETURN_STRING(method); + } } static PHP_METHOD(swoole_http_request, isCompleted) { @@ -1003,5 +1012,3 @@ static PHP_METHOD(swoole_http_request, isCompleted) { } RETURN_BOOL(ctx->completed); } - -static PHP_METHOD(swoole_http_request, __destruct) {} diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 71961971349..ec05b9d61a3 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -10,21 +10,16 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_http_server.h" - #include "swoole_util.h" -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_response_arginfo.h" +END_EXTERN_C() using swoole::Connection; using swoole::Server; @@ -34,6 +29,7 @@ using swoole::coroutine::Socket; using HttpResponse = swoole::http::Response; using HttpContext = swoole::http::Context; +using HttpCookie = swoole::http::Cookie; namespace WebSocket = swoole::websocket; namespace HttpServer = swoole::http_server; @@ -41,9 +37,6 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; -static void http_build_header(HttpContext *ctx, String *response, size_t body_length); -static ssize_t http_build_trailer(HttpContext *ctx, String *response); - static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -64,21 +57,22 @@ static inline void http_header_key_format(char *key, int length) { String *HttpContext::get_write_buffer() { if (co_socket) { - String *buffer = ((Socket *) private_data)->get_write_buffer(); - if (buffer != nullptr) { - return buffer; + return ((Socket *) private_data)->get_write_buffer(); + } else { + if (!write_buffer) { + write_buffer = new String(SW_BUFFER_SIZE_STD, sw_php_allocator()); } + return write_buffer; } - return swoole_http_buffer; } -typedef struct { +struct HttpResponseObject { HttpContext *ctx; zend_object std; -} http_response_t; +}; -static sw_inline http_response_t *php_swoole_http_response_fetch_object(zend_object *obj) { - return (http_response_t *) ((char *) obj - swoole_http_response_handlers.offset); +static sw_inline HttpResponseObject *php_swoole_http_response_fetch_object(zend_object *obj) { + return (HttpResponseObject *) ((char *) obj - swoole_http_response_handlers.offset); } HttpContext *php_swoole_http_response_get_context(zval *zobject) { @@ -90,32 +84,25 @@ void php_swoole_http_response_set_context(zval *zobject, HttpContext *ctx) { } static void php_swoole_http_response_free_object(zend_object *object) { - http_response_t *response = php_swoole_http_response_fetch_object(object); + HttpResponseObject *response = php_swoole_http_response_fetch_object(object); HttpContext *ctx = response->ctx; zval ztmp; /* bool, not required to release it */ if (ctx) { - if (!ctx->end_ && !ctx->detached && sw_reactor()) { + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); + } + if (!ctx->end_ && (ctx->send_chunked || !ctx->send_header_) && !ctx->detached && sw_reactor()) { if (ctx->response.status == 0) { ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR; } - -#ifdef SW_USE_HTTP2 if (ctx->http2) { if (ctx->stream) { ctx->http2_end(nullptr, &ztmp); } - } else -#endif - { - if (ctx->co_socket) { + } else { + if (ctx->is_available()) { ctx->end(nullptr, &ztmp); - } else { - Server *serv = (Server *) ctx->private_data; - Connection *conn = serv->get_connection_by_session_id(ctx->fd); - if (conn && !conn->closed && !conn->peer_closed) { - ctx->end(nullptr, &ztmp); - } } } } @@ -127,7 +114,7 @@ static void php_swoole_http_response_free_object(zend_object *object) { } static zend_object *php_swoole_http_response_create_object(zend_class_entry *ce) { - http_response_t *response = (http_response_t *) zend_object_alloc(sizeof(http_response_t), ce); + HttpResponseObject *response = (HttpResponseObject *) zend_object_alloc(sizeof(HttpResponseObject), ce); zend_object_std_init(&response->std, ce); object_properties_init(&response->std, ce); response->std.handlers = &swoole_http_response_handlers; @@ -153,124 +140,54 @@ static PHP_METHOD(swoole_http_response, upgrade); static PHP_METHOD(swoole_http_response, push); static PHP_METHOD(swoole_http_response, recv); static PHP_METHOD(swoole_http_response, close); -#ifdef SW_USE_HTTP2 static PHP_METHOD(swoole_http_response, trailer); static PHP_METHOD(swoole_http_response, ping); static PHP_METHOD(swoole_http_response, goaway); -#endif static PHP_METHOD(swoole_http_response, status); -static PHP_METHOD(swoole_http_response, __destruct); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_status, 0, 0, 1) - ZEND_ARG_INFO(0, http_code) - ZEND_ARG_INFO(0, reason) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_header, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, format) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_HTTP2 -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_trailer, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_cookie, 0, 0, 1) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expires) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, secure) - ZEND_ARG_INFO(0, httponly) - ZEND_ARG_INFO(0, samesite) - ZEND_ARG_INFO(0, priority) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_write, 0, 0, 1) - ZEND_ARG_INFO(0, content) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_end, 0, 0, 0) - ZEND_ARG_INFO(0, content) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_redirect, 0, 0, 1) - ZEND_ARG_INFO(0, location) - ZEND_ARG_INFO(0, http_code) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_create, 0, 0, 1) - ZEND_ARG_INFO(0, server) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_http_response_methods[] = { - PHP_ME(swoole_http_response, initHeader, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, isWritable, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, cookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, rawcookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, status, arginfo_swoole_http_response_status, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_swoole_http_response_status, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, header, arginfo_swoole_http_response_header, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_swoole_http_response_header, ZEND_ACC_PUBLIC) -#ifdef SW_USE_HTTP2 - PHP_ME(swoole_http_response, trailer, arginfo_swoole_http_response_trailer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, ping, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, goaway, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_http_response, write, arginfo_swoole_http_response_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, end, arginfo_swoole_http_response_end, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, sendfile, arginfo_swoole_http_response_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, redirect, arginfo_swoole_http_response_redirect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, detach, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, create, arginfo_swoole_http_response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_http_response, initHeader, arginfo_class_Swoole_Http_Response_initHeader, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, isWritable, arginfo_class_Swoole_Http_Response_isWritable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setRawCookie, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, trailer, arginfo_class_Swoole_Http_Response_trailer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, ping, arginfo_class_Swoole_Http_Response_ping, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, goaway, arginfo_class_Swoole_Http_Response_goaway, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, write, arginfo_class_Swoole_Http_Response_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, end, arginfo_class_Swoole_Http_Response_end, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, sendfile, arginfo_class_Swoole_Http_Response_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, redirect, arginfo_class_Swoole_Http_Response_redirect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, detach, arginfo_class_Swoole_Http_Response_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, create, arginfo_class_Swoole_Http_Response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /** * WebSocket */ - PHP_ME(swoole_http_response, upgrade, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, push, arginfo_swoole_http_response_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, recv, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, close, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, __destruct, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, upgrade, arginfo_class_Swoole_Http_Response_upgrade, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, push, arginfo_class_Swoole_Http_Response_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, recv, arginfo_class_Swoole_Http_Response_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, close, arginfo_class_Swoole_Http_Response_close, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_http_response_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_http_response, "Swoole\\Http\\Response", "swoole_http_response", nullptr, swoole_http_response_methods); + SW_INIT_CLASS_ENTRY(swoole_http_response, "Swoole\\Http\\Response", nullptr, swoole_http_response_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_response); SW_SET_CLASS_CLONEABLE(swoole_http_response, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_response, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_response, php_swoole_http_response_create_object, php_swoole_http_response_free_object, - http_response_t, + HttpResponseObject, std); zend_declare_property_long(swoole_http_response_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); @@ -282,239 +199,260 @@ void php_swoole_http_response_minit(int module_number) { static PHP_METHOD(swoole_http_response, write) { zval *zdata; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zdata) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } -#ifdef SW_USE_HTTP2 - if (ctx->http2) { - php_swoole_error(E_WARNING, "HTTP2 client does not support HTTP-CHUNK"); - RETURN_FALSE; - } -#endif - #ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - - String *http_buffer = ctx->get_write_buffer(); - - if (!ctx->send_header_) { - ctx->send_chunked = 1; - http_buffer->clear(); - http_build_header(ctx, http_buffer, 0); - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->send_chunked = 0; - ctx->send_header_ = 0; - RETURN_FALSE; - } - } - - struct { - char *str; - size_t length; - } http_body; - size_t length = php_swoole_get_send_data(zdata, &http_body.str); - - if (length == 0) { - php_swoole_error(E_WARNING, "data to send is empty"); - RETURN_FALSE; - } else { - http_body.length = length; - } - // Why not enable compression? // If both compression and chunked encoding are enabled, // then the content stream is first compressed, then chunked; // so the chunk encoding itself is not compressed, // **and the data in each chunk is not compressed individually.** // The remote endpoint then decodes the stream by concatenating the chunks and decompressing the result. - http_buffer->clear(); - char *hex_string = swoole_dec2hex(http_body.length, 16); - int hex_len = strlen(hex_string); - //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str - http_buffer->append(hex_string, hex_len); - http_buffer->append(ZEND_STRL("\r\n")); - http_buffer->append(http_body.str, http_body.length); - http_buffer->append(ZEND_STRL("\r\n")); - sw_free(hex_string); + ctx->accept_compression = 0; +#endif - RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); + if (ctx->http2) { + ctx->http2_write(zdata, return_value); + } else { + ctx->write(zdata, return_value); + } } -static bool parse_header_flags(HttpContext *ctx, const char *key, size_t keylen, uint32_t &header_flags) { +static int parse_header_name(const char *key, size_t keylen) { if (SW_STRCASEEQ(key, keylen, "Server")) { - header_flags |= HTTP_HEADER_SERVER; + return HTTP_HEADER_SERVER; } else if (SW_STRCASEEQ(key, keylen, "Connection")) { - header_flags |= HTTP_HEADER_CONNECTION; + return HTTP_HEADER_CONNECTION; } else if (SW_STRCASEEQ(key, keylen, "Date")) { - header_flags |= HTTP_HEADER_DATE; + return HTTP_HEADER_DATE; } else if (SW_STRCASEEQ(key, keylen, "Content-Length")) { - header_flags |= HTTP_HEADER_CONTENT_LENGTH; + return HTTP_HEADER_CONTENT_LENGTH; } else if (SW_STRCASEEQ(key, keylen, "Content-Type")) { - header_flags |= HTTP_HEADER_CONTENT_TYPE; + return HTTP_HEADER_CONTENT_TYPE; } else if (SW_STRCASEEQ(key, keylen, "Transfer-Encoding")) { - header_flags |= HTTP_HEADER_TRANSFER_ENCODING; + return HTTP_HEADER_TRANSFER_ENCODING; + } else if (SW_STRCASEEQ(key, keylen, "Content-Encoding")) { + return HTTP_HEADER_CONTENT_ENCODING; } - return true; + return 0; } -static void http_build_header(HttpContext *ctx, String *response, size_t body_length) { - char *buf = sw_tg_buffer()->str; - size_t l_buf = sw_tg_buffer()->size; - int n; - char *date_str; +static void http_set_date_header(String *response) { + static SW_THREAD_LOCAL struct { + time_t time; + zend_string *date = nullptr; + } cache{}; - assert(ctx->send_header_ == 0); + time_t now = time(nullptr); + if (now != cache.time) { + if (cache.date) { + zend_string_release(cache.date); + } - /** - * http status line - */ - if (!ctx->response.reason) { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %s\r\n", HttpServer::get_status_message(ctx->response.status)); + cache.time = now; + cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + } + response->append(ZEND_STRL("Date: ")); + response->append(ZSTR_VAL(cache.date), ZSTR_LEN(cache.date)); + response->append(ZEND_STRL("\r\n")); +} + +static void add_custom_header(String *http_buffer, const char *key, size_t l_key, zval *value) { + if (ZVAL_IS_NULL(value)) { + return; + } + + zend::String str_value(value); + str_value.rtrim(); + if (swoole_http_has_crlf(str_value.val(), str_value.len())) { + return; + } + + http_buffer->append(key, l_key); + http_buffer->append(SW_STRL(": ")); + http_buffer->append(str_value.val(), str_value.len()); + http_buffer->append(SW_STRL("\r\n")); +} + +void HttpContext::build_header(String *http_buffer, const char *body, size_t length) { + assert(send_header_ == 0); + + // http status line + http_buffer->append(ZEND_STRL("HTTP/1.1 ")); + if (response.reason) { + http_buffer->append(response.status); + http_buffer->append(ZEND_STRL(" ")); + http_buffer->append(response.reason, strlen(response.reason)); } else { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %d %s\r\n", ctx->response.status, ctx->response.reason); + const char *status = HttpServer::get_status_message(response.status); + http_buffer->append((char *) status, strlen(status)); } - response->append(buf, n); + http_buffer->append(ZEND_STRL("\r\n")); + // http headers uint32_t header_flags = 0x0; - - /** - * http header - */ zval *zheader = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); if (ZVAL_IS_ARRAY(zheader)) { - const char *key; - uint32_t keylen; - int type; +#ifdef SW_HAVE_COMPRESSION + zend_string *content_type = nullptr; +#endif zval *zvalue; + zend_string *string_key; + zend_ulong num_key; - auto add_header = [](swoole::String *response, const char *key, size_t l_key, zval *value) { - if (ZVAL_IS_NULL(value)) { - return; - } - zend::String str_value(value); - str_value.rtrim(); - if (swoole_http_has_crlf(str_value.val(), str_value.len())) { - return; - } - response->append(key, l_key); - response->append(SW_STRL(": ")); - response->append(str_value.val(), str_value.len()); - response->append(SW_STRL("\r\n")); - }; - - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { - // TODO: numeric key name neccessary? - if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { - continue; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zheader), num_key, string_key, zvalue) { + if (!string_key) { + string_key = zend_long_to_str(num_key); + } else { + zend_string_addref(string_key); } - if (!parse_header_flags(ctx, key, keylen, header_flags)) { - continue; + zend::String key(string_key, false); + + int key_header = parse_header_name(ZSTR_VAL(string_key), ZSTR_LEN(string_key)); + + if (key_header > 0) { +#ifdef SW_HAVE_COMPRESSION + if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { + content_type = zval_get_string(zvalue); + } + if (key_header == HTTP_HEADER_CONTENT_ENCODING && ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) { + accept_compression = 0; + } + // https://github.com/swoole/swoole-src/issues/4857 + // https://github.com/swoole/swoole-src/issues/5026 + if (key_header == HTTP_HEADER_CONTENT_LENGTH && accept_compression) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_CONFLICT_HEADER, + "The client has set 'Accept-Encoding', 'Content-Length' will be ignored"); + continue; + } +#endif + // https://github.com/swoole/swoole-src/issues/4857 + // https://github.com/swoole/swoole-src/issues/5026 + if (key_header == HTTP_HEADER_CONTENT_LENGTH && send_chunked) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_CONFLICT_HEADER, + "You have set 'Transfer-Encoding', 'Content-Length' will be ignored"); + continue; + } + + header_flags |= key_header; + if (ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) { + continue; + } } if (ZVAL_IS_ARRAY(zvalue)) { zval *zvalue_2; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) { - add_header(response, key, keylen, zvalue_2); + add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2); } SW_HASHTABLE_FOREACH_END(); } else { - add_header(response, key, keylen, zvalue); + add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); } } - SW_HASHTABLE_FOREACH_END(); - (void) type; + ZEND_HASH_FOREACH_END(); + +#ifdef SW_HAVE_COMPRESSION + if (accept_compression && compression_types) { + std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) + : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); + accept_compression = compression_types->find(str_content_type) != compression_types->end(); + if (content_type) { + zend_string_release(content_type); + } + } +#endif } // http cookies zval *zcookie = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); if (ZVAL_IS_ARRAY(zcookie)) { zval *zvalue; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) { - if (Z_TYPE_P(zvalue) != IS_STRING) { + if (Z_TYPE_P(zvalue) != IS_STRING || swoole_http_has_crlf(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue))) { continue; } - response->append(ZEND_STRL("Set-Cookie: ")); - response->append(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - response->append(ZEND_STRL("\r\n")); + http_buffer->append(ZEND_STRL("Set-Cookie: ")); + http_buffer->append(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); + http_buffer->append(ZEND_STRL("\r\n")); } SW_HASHTABLE_FOREACH_END(); } if (!(header_flags & HTTP_HEADER_SERVER)) { - response->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); + http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); + } + + if (!(header_flags & HTTP_HEADER_DATE)) { + http_set_date_header(http_buffer); } // websocket protocol (subsequent header info is unnecessary) - if (ctx->upgrade == 1) { - response->append(ZEND_STRL("\r\n")); - ctx->send_header_ = 1; + if (upgrade == 1) { + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; return; } - if (!(header_flags & HTTP_HEADER_CONNECTION)) { - if (ctx->keepalive) { - response->append(ZEND_STRL("Connection: keep-alive\r\n")); + if (keepalive) { + http_buffer->append(ZEND_STRL("Connection: keep-alive\r\n")); } else { - response->append(ZEND_STRL("Connection: close\r\n")); + http_buffer->append(ZEND_STRL("Connection: close\r\n")); } } if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { - response->append(ZEND_STRL("Content-Type: text/html\r\n")); + http_buffer->append(ZEND_STRL("Content-Type: " SW_HTTP_DEFAULT_CONTENT_TYPE "\r\n")); } - if (!(header_flags & HTTP_HEADER_DATE)) { - date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), time(nullptr), 0); - n = sw_snprintf(buf, l_buf, "Date: %s\r\n", date_str); - response->append(buf, n); - efree(date_str); - } - - if (ctx->send_chunked) { - SW_ASSERT(body_length == 0); + if (send_chunked) { + SW_ASSERT(length == 0); if (!(header_flags & HTTP_HEADER_TRANSFER_ENCODING)) { - response->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); + http_buffer->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); } } // Content-Length - else if (body_length > 0 || ctx->parser.method != PHP_HTTP_HEAD) { + else if (length > 0 || parser.method != PHP_HTTP_HEAD) { #ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - body_length = swoole_zlib_buffer->length; + if (compress(body, length)) { + length = zlib_buffer->length; + const char *content_encoding = get_content_encoding(); + http_buffer->append(ZEND_STRL("Content-Encoding: ")); + http_buffer->append((char *) content_encoding, strlen(content_encoding)); + http_buffer->append(ZEND_STRL("\r\n")); } #endif if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { - n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", body_length); - response->append(buf, n); + http_buffer->append(ZEND_STRL("Content-Length: ")); + + char result[128]; + int convert_result = swoole_itoa(result, length); + http_buffer->append(result, convert_result); + http_buffer->append(ZEND_STRL("\r\n")); } } -#ifdef SW_HAVE_COMPRESSION - // http compress - if (ctx->accept_compression) { - const char *content_encoding = ctx->get_content_encoding(); - response->append(ZEND_STRL("Content-Encoding: ")); - response->append((char *) content_encoding, strlen(content_encoding)); - response->append(ZEND_STRL("\r\n")); - } -#endif - response->append(ZEND_STRL("\r\n")); - ctx->send_header_ = 1; + + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; } -static ssize_t http_build_trailer(HttpContext *ctx, String *response) { +ssize_t HttpContext::build_trailer(String *http_buffer) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; int n; ssize_t ret = 0; zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); uint32_t size = php_swoole_array_length_safe(ztrailer); if (size > 0) { @@ -532,13 +470,13 @@ static ssize_t http_build_trailer(HttpContext *ctx, String *response) { zend::String str_value(zvalue); n = sw_snprintf( buf, l_buf, "%.*s: %.*s\r\n", (int) keylen, key, (int) str_value.len(), str_value.val()); - response->append(buf, n); + http_buffer->append(buf, n); ret += n; } } SW_HASHTABLE_FOREACH_END(); (void) type; - response->append(ZEND_STRL("\r\n")); + http_buffer->append(ZEND_STRL("\r\n")); } return ret; @@ -565,44 +503,45 @@ void php_brotli_free(void *opaque, void *address) { #endif #ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(const char *data, size_t length, int method, int level) { +bool HttpContext::compress(const char *data, size_t length) { #ifdef SW_HAVE_ZLIB int encoding; #endif + if (!accept_compression || length < compression_min_length) { + return false; + } + if (0) { + return false; } #ifdef SW_HAVE_ZLIB // gzip: 0x1f - else if (method == HTTP_COMPRESS_GZIP) { + else if (compression_method == HTTP_COMPRESS_GZIP) { encoding = 0x1f; } // deflate: -0xf - else if (method == HTTP_COMPRESS_DEFLATE) { + else if (compression_method == HTTP_COMPRESS_DEFLATE) { encoding = -0xf; } #endif #ifdef SW_HAVE_BROTLI - else if (method == HTTP_COMPRESS_BR) { - if (level < BROTLI_MIN_QUALITY) { - level = BROTLI_MIN_QUALITY; - } else if (level > BROTLI_MAX_QUALITY) { - level = BROTLI_MAX_QUALITY; + else if (compression_method == HTTP_COMPRESS_BR) { + if (compression_level < BROTLI_MIN_QUALITY) { + compression_level = BROTLI_MIN_QUALITY; + } else if (compression_level > BROTLI_MAX_QUALITY) { + compression_level = BROTLI_MAX_QUALITY; } size_t memory_size = BrotliEncoderMaxCompressedSize(length); - if (memory_size > swoole_zlib_buffer->size) { - if (!swoole_zlib_buffer->extend(memory_size)) { - return SW_ERR; - } - } + zlib_buffer = std::make_shared(memory_size); size_t input_size = length; const uint8_t *input_buffer = (const uint8_t *) data; - size_t encoded_size = swoole_zlib_buffer->size; - uint8_t *encoded_buffer = (uint8_t *) swoole_zlib_buffer->str; + size_t encoded_size = zlib_buffer->size; + uint8_t *encoded_buffer = (uint8_t *) zlib_buffer->str; - if (BROTLI_TRUE != BrotliEncoderCompress(level, + if (BROTLI_TRUE != BrotliEncoderCompress(compression_level, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, input_size, @@ -610,60 +549,82 @@ int swoole_http_response_compress(const char *data, size_t length, int method, i &encoded_size, encoded_buffer)) { swoole_warning("BrotliEncoderCompress() failed"); - return SW_ERR; + return false; } else { - swoole_zlib_buffer->length = encoded_size; - return SW_OK; + zlib_buffer->length = encoded_size; + content_compressed = 1; + return true; } } +#endif +#ifdef SW_HAVE_ZSTD + else if (compression_method == HTTP_COMPRESS_ZSTD) { + int zstd_compress_level = compression_level; + int zstd_max_level = ZSTD_maxCLevel(); + int zstd_min_level = ZSTD_minCLevel(); + zstd_compress_level = (zstd_compress_level > zstd_max_level) + ? zstd_max_level + : (zstd_compress_level < zstd_min_level ? zstd_min_level : zstd_compress_level); + + size_t compress_size = ZSTD_compressBound(length); + zlib_buffer = std::make_shared(compress_size);; + size_t zstd_compress_result = + ZSTD_compress((void *) zlib_buffer->str, compress_size, (void *) data, length, zstd_compress_level); + + if (ZSTD_isError(zstd_compress_result)) { + swoole_warning("ZSTD_compress() failed, Error: [%s]", ZSTD_getErrorName(zstd_compress_result)); + return false; + } + + zlib_buffer->length = zstd_compress_result; + content_compressed = 1; + return true; + } #endif else { swoole_warning("Unknown compression method"); - return SW_ERR; + return false; } #ifdef SW_HAVE_ZLIB - if (level < Z_NO_COMPRESSION) { - level = Z_DEFAULT_COMPRESSION; - } else if (level == Z_NO_COMPRESSION) { - level = Z_BEST_SPEED; - } else if (level > Z_BEST_COMPRESSION) { - level = Z_BEST_COMPRESSION; - } - - size_t memory_size = ((size_t)((double) length * (double) 1.015)) + 10 + 8 + 4 + 1; - if (memory_size > swoole_zlib_buffer->size) { - if (!swoole_zlib_buffer->extend(memory_size)) { - return SW_ERR; - } + if (compression_level < Z_NO_COMPRESSION) { + compression_level = Z_DEFAULT_COMPRESSION; + } else if (compression_level == Z_NO_COMPRESSION) { + compression_level = Z_BEST_SPEED; + } else if (compression_level > Z_BEST_COMPRESSION) { + compression_level = Z_BEST_COMPRESSION; } + size_t memory_size = ((size_t) ((double) length * (double) 1.015)) + 10 + 8 + 4 + 1; + zlib_buffer = std::make_shared(memory_size); + z_stream zstream = {}; int status; zstream.zalloc = php_zlib_alloc; zstream.zfree = php_zlib_free; - status = deflateInit2(&zstream, level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + status = deflateInit2(&zstream, compression_level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (status != Z_OK) { swoole_warning("deflateInit2() failed, Error: [%d]", status); - return SW_ERR; + return false; } zstream.next_in = (Bytef *) data; zstream.avail_in = length; - zstream.next_out = (Bytef *) swoole_zlib_buffer->str; - zstream.avail_out = swoole_zlib_buffer->size; + zstream.next_out = (Bytef *) zlib_buffer->str; + zstream.avail_out = zlib_buffer->size; status = deflate(&zstream, Z_FINISH); deflateEnd(&zstream); if (status != Z_STREAM_END) { swoole_warning("deflate() failed, Error: [%d]", status); - return SW_ERR; + return false; } - swoole_zlib_buffer->length = zstream.total_out; - swoole_zlib_buffer->offset = 0; - return SW_OK; + zlib_buffer->length = zstream.total_out; + zlib_buffer->offset = 0; + content_compressed = 1; + return true; #endif } #endif @@ -704,12 +665,17 @@ static PHP_METHOD(swoole_http_response, end) { Z_PARAM_ZVAL_EX(zdata, 1, 0) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_USE_HTTP2 + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); + } + + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); + } + if (ctx->http2) { ctx->http2_end(zdata, return_value); - } else -#endif - { + } else { ctx->end(zdata, return_value); } } @@ -718,7 +684,7 @@ void HttpContext::send_trailer(zval *return_value) { String *http_buffer = get_write_buffer(); http_buffer->clear(); - if (http_build_trailer(this, http_buffer) == 0) { + if (build_trailer(http_buffer) == 0) { return; } if (!send(this, http_buffer->str, http_buffer->length)) { @@ -728,19 +694,85 @@ void HttpContext::send_trailer(zval *return_value) { } } -void HttpContext::end(zval *zdata, zval *return_value) { - struct { - char *str; - size_t length; - } http_body; - if (zdata) { - http_body.length = php_swoole_get_send_data(zdata, &http_body.str); - } else { - http_body.length = 0; - http_body.str = nullptr; +bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("Content-Type"))) { + add_assoc_string(zheader, "Content-Type", (char *) swoole::mime_type::get(file).c_str()); } + if (!send_header_) { +#ifdef SW_HAVE_COMPRESSION + accept_compression = 0; +#endif + String *http_buffer = get_write_buffer(); + http_buffer->clear(); + + build_header(http_buffer, nullptr, length); + + if (!send(this, http_buffer->str, http_buffer->length)) { + send_header_ = 0; + return false; + } + } + + if (length > 0 && !sendfile(this, file, l_file, offset, length)) { + close(this); + return false; + } + + end_ = 1; + + if (!keepalive) { + close(this); + } + return true; +} + +void HttpContext::write(zval *zdata, zval *return_value) { + String *http_buffer = get_write_buffer(); + + if (!send_header_) { + send_chunked = 1; + http_buffer->clear(); + build_header(http_buffer, nullptr, 0); + if (!send(this, http_buffer->str, http_buffer->length)) { + send_chunked = 0; + send_header_ = 0; + RETURN_FALSE; + } + } + + char *data = nullptr; + size_t length = php_swoole_get_send_data(zdata, &data); + + if (length == 0) { + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); + RETURN_FALSE; + } + + http_buffer->clear(); + char *hex_string = swoole_dec2hex(length, 16); + int hex_len = strlen(hex_string); + //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str + http_buffer->append(hex_string, hex_len); + http_buffer->append(ZEND_STRL("\r\n")); + http_buffer->append(data, length); + http_buffer->append(ZEND_STRL("\r\n")); + sw_free(hex_string); + + RETURN_BOOL(send(this, http_buffer->str, http_buffer->length)); +} + +void HttpContext::end(zval *zdata, zval *return_value) { if (send_chunked) { + if (zdata && Z_STRLEN_P(zdata) > 0) { + zval retval; + write(zdata, &retval); + if (ZVAL_IS_FALSE(&retval)) { + RETURN_FALSE; + } + } if (send_trailer_) { if (!send(this, ZEND_STRL("0\r\n"))) { RETURN_FALSE; @@ -753,54 +785,66 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } send_chunked = 0; - } - // no http chunk - else { - String *http_buffer = get_write_buffer(); + } else { + char *data = nullptr; + size_t length = zdata ? php_swoole_get_send_data(zdata, &data) : 0; + String *http_buffer = get_write_buffer(); http_buffer->clear(); -#ifdef SW_HAVE_COMPRESSION - if (accept_compression) { - if (http_body.length == 0 || http_body.length < compression_min_length || - swoole_http_response_compress(http_body.str, http_body.length, compression_method, compression_level) != - SW_OK) { - accept_compression = 0; + +#ifdef SW_HAVE_ZLIB + if (upgrade) { + Server *serv = nullptr; + Connection *conn = nullptr; + if (!co_socket) { + serv = (Server *) private_data; + conn = serv->get_connection_verify(fd); + } + bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression; + bool accept_websocket_compression = false; + zval *pData; + if (enable_websocket_compression && request.zobject && + (pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) && + Z_TYPE_P(pData) == IS_STRING) { + std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); + if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { + accept_websocket_compression = true; + set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); + } + } + websocket_compression = accept_websocket_compression; + if (conn) { + conn->websocket_compression = accept_websocket_compression; } } #endif - http_build_header(this, http_buffer, http_body.length); - char *send_body_str; - size_t send_body_len; + build_header(http_buffer, data, length); - if (http_body.length > 0) { + if (length > 0) { #ifdef SW_HAVE_COMPRESSION - if (accept_compression) { - send_body_str = swoole_zlib_buffer->str; - send_body_len = swoole_zlib_buffer->length; - } else -#endif - { - send_body_str = http_body.str; - send_body_len = http_body.length; + if (content_compressed) { + data = zlib_buffer->str; + length = zlib_buffer->length; } +#endif // send twice to reduce memory copy - if (send_body_len < SwooleG.pagesize) { - if (http_buffer->append(send_body_str, send_body_len) < 0) { - send_header_ = 0; - RETURN_FALSE; - } - } else { + if (length > SW_HTTP_MAX_APPEND_DATA) { if (!send(this, http_buffer->str, http_buffer->length)) { send_header_ = 0; RETURN_FALSE; } - if (!send(this, send_body_str, send_body_len)) { + if (!send(this, data, length)) { end_ = 1; close(this); RETURN_FALSE; } goto _skip_copy; + } else { + if (http_buffer->append(data, length) < 0) { + send_header_ = 0; + RETURN_FALSE; + } } } @@ -815,6 +859,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { if (upgrade && !co_socket) { Server *serv = (Server *) private_data; Connection *conn = serv->get_connection_verify(fd); + if (conn && conn->websocket_status == websocket::STATUS_HANDSHAKE) { if (response.status == 101) { conn->websocket_status = websocket::STATUS_ACTIVE; @@ -833,21 +878,17 @@ void HttpContext::end(zval *zdata, zval *return_value) { } bool HttpContext::set_header(const char *k, size_t klen, const char *v, size_t vlen, bool format) { - zval ztmp; - ZVAL_STRINGL(&ztmp, v, vlen); - Z_ADDREF(ztmp); - return set_header(k, klen, &ztmp, format); + zend::Variable ztmp(v, vlen); + return set_header(k, klen, ztmp.ptr(), format); } bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool format) { if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) { php_swoole_error(E_WARNING, "header key is too long"); - Z_TRY_DELREF_P(zvalue); return false; } if (swoole_http_has_crlf(k, klen)) { - Z_TRY_DELREF_P(zvalue); return false; } @@ -855,17 +896,14 @@ bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool form swoole_http_response_ce, response.zobject, &response.zheader, ZEND_STRL("header")); if (format) { swoole_strlcpy(sw_tg_buffer()->str, k, SW_HTTP_HEADER_KEY_SIZE); -#ifdef SW_USE_HTTP2 if (http2) { swoole_strtolower(sw_tg_buffer()->str, klen); - } else -#endif - { + } else { http_header_key_format(sw_tg_buffer()->str, klen); } k = sw_tg_buffer()->str; } - add_assoc_zval_ex(zheader, k, klen, zvalue); + zend::array_set(zheader, k, klen, zvalue); return true; } @@ -885,9 +923,12 @@ static PHP_METHOD(swoole_http_response, sendfile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &file, &l_file, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(file, l_file) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_file == 0) { php_swoole_error(E_WARNING, "file name is empty"); @@ -899,6 +940,11 @@ static PHP_METHOD(swoole_http_response, sendfile) { php_swoole_sys_error(E_WARNING, "stat(%s) failed", file); RETURN_FALSE; } + if (!S_ISREG(file_stat.st_mode)) { + php_swoole_error(E_WARNING, "parameter $file[%s] given is not a regular file", file); + swoole_set_last_error(SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); + RETURN_FALSE; + } if (file_stat.st_size < offset) { php_swoole_error(E_WARNING, "parameter $offset[" ZEND_LONG_FMT "] exceeds the file size", offset); RETURN_FALSE; @@ -911,154 +957,88 @@ static PHP_METHOD(swoole_http_response, sendfile) { length = file_stat.st_size - offset; } -#ifdef SW_USE_HTTP2 - if (!ctx->http2) -#endif - if (!ctx->send_header_) { -#ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - String *http_buffer = ctx->get_write_buffer(); - - http_buffer->clear(); - - zval *zheader = sw_zend_read_and_convert_property_array( - swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); - if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("Content-Type"))) { - add_assoc_string(zheader, "Content-Type", (char *) swoole::mime_type::get(file).c_str()); - } - - http_build_header(ctx, http_buffer, length); - - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->send_header_ = 0; - RETURN_FALSE; - } - } - - if (length != 0) { - if (!ctx->sendfile(ctx, file, l_file, offset, length)) { - ctx->close(ctx); - RETURN_FALSE; - } + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); } + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); + } + if (ctx->http2) { + RETURN_BOOL(ctx->http2_send_file(file, l_file, offset, length)); + } else { + RETURN_BOOL(ctx->send_file(file, l_file, offset, length)); + } +} - ctx->end_ = 1; +static bool inline php_swoole_http_response_create_cookie(HttpCookie *cookie, zval *zobject) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(zobject); - if (!ctx->keepalive) { - ctx->close(ctx); + zend_string *cookie_str = cookie->toString(); + if (!cookie_str) { + cookie->reset(); + return false; } - RETURN_TRUE; + add_next_index_str( + swoole_http_init_and_read_property( + swoole_http_response_ce, ctx->response.zobject, &ctx->response.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)), + cookie_str); + + return true; } -static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool url_encode) { - char *name = nullptr, *value = nullptr, *path = nullptr, *domain = nullptr, *samesite = nullptr, - *priority = nullptr; +static void php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool encode) { + zval *name_or_object; + zend_string *value = nullptr, *path = nullptr, *domain = nullptr, *sameSite = nullptr, *priority = nullptr; zend_long expires = 0; - size_t name_len, value_len = 0, path_len = 0, domain_len = 0, samesite_len = 0, priority_len = 0; - zend_bool secure = 0, httponly = 0; + zend_bool secure = false, httpOnly = false, partitioned = false; + bool result; - ZEND_PARSE_PARAMETERS_START(1, 9) - Z_PARAM_STRING(name, name_len) + ZEND_PARSE_PARAMETERS_START(1, 10) + Z_PARAM_ZVAL(name_or_object) Z_PARAM_OPTIONAL - Z_PARAM_STRING(value, value_len) + Z_PARAM_STR(value) Z_PARAM_LONG(expires) - Z_PARAM_STRING(path, path_len) - Z_PARAM_STRING(domain, domain_len) + Z_PARAM_STR(path) + Z_PARAM_STR(domain) Z_PARAM_BOOL(secure) - Z_PARAM_BOOL(httponly) - Z_PARAM_STRING(samesite, samesite_len) - Z_PARAM_STRING(priority, priority_len) + Z_PARAM_BOOL(httpOnly) + Z_PARAM_STR(sameSite) + Z_PARAM_STR(priority) + Z_PARAM_BOOL(partitioned) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) { - RETURN_FALSE; - } - - size_t cookie_size = name_len /* + value_len */ + path_len + domain_len + 100; - char *cookie = nullptr, *date = nullptr; - - if (name_len > 0 && strpbrk(name, "=,; \t\r\n\013\014") != nullptr) { - php_swoole_error(E_WARNING, "Cookie names can't contain any of the following '=,; \\t\\r\\n\\013\\014'"); - RETURN_FALSE; - } - - if (!url_encode && swoole_http_has_crlf(value, value_len)) { - RETURN_FALSE; - } - - if (value_len == 0) { - cookie = (char *) emalloc(cookie_size); - date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); - snprintf(cookie, cookie_size, "%s=deleted; expires=%s", name, date); - efree(date); + if (ZVAL_IS_STRING(name_or_object)) { + HttpCookie cookie(encode); + (&cookie) + ->withName(Z_STR_P(name_or_object)) + ->withValue(value) + ->withExpires(expires) + ->withPath(path) + ->withDomain(domain) + ->withSecure(secure) + ->withHttpOnly(httpOnly) + ->withSameSite(sameSite) + ->withPriority(priority) + ->withPartitioned(partitioned); + result = php_swoole_http_response_create_cookie(&cookie, ZEND_THIS); + } else if (ZVAL_IS_OBJECT(name_or_object)) { + HttpCookie *cookie = php_swoole_http_get_cooke_safety(name_or_object); + result = php_swoole_http_response_create_cookie(cookie, ZEND_THIS); } else { - if (url_encode) { - char *encoded_value; - int encoded_value_len; - encoded_value = php_swoole_url_encode(value, value_len, &encoded_value_len); - cookie_size += encoded_value_len; - cookie = (char *) emalloc(cookie_size); - sw_snprintf(cookie, cookie_size, "%s=%s", name, encoded_value); - efree(encoded_value); - } else { - cookie_size += value_len; - cookie = (char *) emalloc(cookie_size); - sw_snprintf(cookie, cookie_size, "%s=%s", name, value); - } - if (expires > 0) { - strlcat(cookie, "; expires=", cookie_size); - date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), expires, 0); - const char *p = (const char *) zend_memrchr(date, '-', strlen(date)); - if (!p || *(p + 5) != ' ') { - php_swoole_error(E_WARNING, "Expiry date can't be a year greater than 9999"); - efree(date); - efree(cookie); - RETURN_FALSE; - } - strlcat(cookie, date, cookie_size); - efree(date); - } + php_swoole_error(E_WARNING, "The first argument must be a string or an cookie object"); + result = false; } - if (path_len > 0) { - strlcat(cookie, "; path=", cookie_size); - strlcat(cookie, path, cookie_size); - } - if (domain_len > 0) { - strlcat(cookie, "; domain=", cookie_size); - strlcat(cookie, domain, cookie_size); - } - if (secure) { - strlcat(cookie, "; secure", cookie_size); - } - if (httponly) { - strlcat(cookie, "; httponly", cookie_size); - } - if (samesite_len > 0) { - strlcat(cookie, "; samesite=", cookie_size); - strlcat(cookie, samesite, cookie_size); - } - if (priority_len > 0) { - strlcat(cookie, "; priority=", cookie_size); - strlcat(cookie, priority, cookie_size); - } - add_next_index_stringl( - swoole_http_init_and_read_property( - swoole_http_response_ce, ctx->response.zobject, &ctx->response.zcookie, ZEND_STRL("cookie")), - cookie, - strlen(cookie)); - efree(cookie); - RETURN_TRUE; + + RETURN_BOOL(result); } static PHP_METHOD(swoole_http_response, cookie) { - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_http_response, rawcookie) { - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_http_response, status) { @@ -1099,11 +1079,9 @@ static PHP_METHOD(swoole_http_response, header) { if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - Z_TRY_ADDREF_P(zvalue); RETURN_BOOL(ctx->set_header(k, klen, zvalue, format)); } -#ifdef SW_USE_HTTP2 static PHP_METHOD(swoole_http_response, trailer) { char *k, *v; size_t klen, vlen; @@ -1156,17 +1134,19 @@ static PHP_METHOD(swoole_http_response, goaway) { php_swoole_fatal_error(E_WARNING, "fd[%ld] is not a HTTP2 conncetion", ctx->fd); RETURN_FALSE; } + zend_long error_code = SW_HTTP2_ERROR_NO_ERROR; char *debug_data = nullptr; size_t debug_data_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(error_code) + Z_PARAM_STRING(debug_data, debug_data_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); SW_CHECK_RETURN(swoole_http2_server_goaway(ctx, error_code, debug_data, debug_data_len)); } -#endif static PHP_METHOD(swoole_http_response, upgrade) { HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); @@ -1300,10 +1280,10 @@ static PHP_METHOD(swoole_http_response, create) { if (instanceof_function(Z_OBJCE_P(zobject), swoole_server_ce)) { serv = php_swoole_server_get_and_check_server(zobject); if (serv->get_connection_verify(fd) == nullptr) { - php_swoole_fatal_error(E_WARNING, "parameter $2 must be valid connection session id"); + php_swoole_fatal_error(E_WARNING, "parameter $2 (%ld) must be valid connection session_id", (long) fd); RETURN_FALSE; } - } else if (instanceof_function(Z_OBJCE_P(zobject), swoole_socket_coro_ce)) { + } else if (sw_zval_is_co_socket(zobject)) { sock = php_swoole_get_socket(zobject); fd = sock->get_fd(); } else { @@ -1360,10 +1340,6 @@ static PHP_METHOD(swoole_http_response, create) { } } - if (sw_unlikely(swoole_http_buffer == nullptr)) { - php_swoole_http_server_init_global_variant(); - } - object_init_ex(return_value, swoole_http_response_ce); php_swoole_http_response_set_context(return_value, ctx); ctx->fd = fd; @@ -1371,7 +1347,8 @@ static PHP_METHOD(swoole_http_response, create) { sw_copy_to_stack(ctx->response.zobject, ctx->response._zobject); zend_update_property_long(swoole_http_response_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("fd"), fd); if (ctx->co_socket) { - zend_update_property(swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), ZEND_STRL("socket"), zobject); + zend_update_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zobject); } if (zrequest) { zend_update_property_long(swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL("fd"), fd); @@ -1409,5 +1386,3 @@ static PHP_METHOD(swoole_http_response, redirect) { } ctx->end(nullptr, return_value); } - -static PHP_METHOD(swoole_http_response, __destruct) {} diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index a7e1bc55e56..7e1eee796e3 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -10,11 +10,15 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_http_server.h" +#include "swoole_process_pool.h" +BEGIN_EXTERN_C() +#include "rfc1867.h" +END_EXTERN_C() using namespace swoole; using swoole::coroutine::Socket; @@ -25,27 +29,36 @@ using HttpContext = swoole::http::Context; namespace WebSocket = swoole::websocket; -String *swoole_http_buffer; -#ifdef SW_HAVE_COMPRESSION -/* not only be used by zlib but also be used by br */ -String *swoole_zlib_buffer; -#endif -String *swoole_http_form_data_buffer; - zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; +static SW_THREAD_LOCAL std::queue queued_http_contexts; +static SW_THREAD_LOCAL std::unordered_map client_ips; + static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); static bool http_context_disconnect(HttpContext *ctx); +static void http_server_process_request(Server *serv, zend::Callable *cb, HttpContext *ctx) { + zval args[2]; + args[0] = *ctx->request.zobject; + args[1] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); +#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); +#endif + ctx->close(ctx); + } +} + int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { SessionId session_id = req->info.fd; int server_fd = req->info.server_fd; Connection *conn = serv->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); return SW_ERR; } @@ -59,14 +72,14 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { if (conn->websocket_status == WebSocket::STATUS_ACTIVE) { return swoole_websocket_onMessage(serv, req); } -#ifdef SW_USE_HTTP2 + if (conn->http2_stream) { return swoole_http2_server_onReceive(serv, conn, req); } -#endif HttpContext *ctx = swoole_http_context_new(session_id); ctx->init(serv); + ctx->onBeforeRequest = swoole_http_server_onBeforeRequest; zval *zdata = &ctx->request.zdata; php_swoole_get_recv_data(serv, zdata, req); @@ -78,9 +91,8 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { (int) Z_STRLEN_P(zdata), Z_STRVAL_P(zdata)); - zval args[2], *zrequest_object = &args[0], *zresponse_object = &args[1]; - args[0] = *ctx->request.zobject; - args[1] = *ctx->response.zobject; + zval *zrequest_object = ctx->request.zobject; + zval *zresponse_object = ctx->response.zobject; swoole_http_parser *parser = &ctx->parser; parser->data = ctx; @@ -88,9 +100,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { size_t parsed_n = ctx->parse(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)); if (ctx->parser.state == s_dead) { -#ifdef SW_HTTP_BAD_REQUEST_PACKET ctx->send(ctx, SW_STRL(SW_HTTP_BAD_REQUEST_PACKET)); -#endif ctx->close(ctx); swoole_notice("request is illegal and it has been discarded, %ld bytes unprocessed", Z_STRLEN_P(zdata) - parsed_n); @@ -100,21 +110,48 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { do { zval *zserver = ctx->request.zserver; Connection *serv_sock = serv->get_connection(conn->server_fd); + HashTable *ht = Z_ARR_P(zserver); + if (serv_sock) { - add_assoc_long(zserver, "server_port", serv_sock->info.get_port()); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) serv_sock->info.get_port()); + } + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) conn->info.get_port()); + + if (conn->info.type == SW_SOCK_TCP && IN_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v4.sin_addr)) { + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V4)); + } else if (conn->info.type == SW_SOCK_TCP6 && IN6_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v6.sin6_addr)) { + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V6)); + } else { + if (serv->is_base_mode() && ctx->keepalive) { + auto iter = client_ips.find(session_id); + if (iter == client_ips.end()) { + auto rs = client_ips.emplace(session_id, conn->info.get_ip()); + iter = rs.first; + } + iter->second.add_ref(); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), iter->second.ptr()); + } else { + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), conn->info.get_ip()); + } } - add_assoc_long(zserver, "remote_port", conn->info.get_port()); - add_assoc_string(zserver, "remote_addr", (char *) conn->info.get_ip()); - add_assoc_long(zserver, "master_time", (int) conn->last_recv_time); + + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (zend_long) conn->last_recv_time); } while (0); + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST, ctx); + } + // begin to check and call registerd callback do { - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *cb = nullptr; if (conn->websocket_status == WebSocket::STATUS_CONNECTION) { - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onHandShake); - if (fci_cache == nullptr) { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onHandshake); + if (cb == nullptr) { swoole_websocket_onHandshake(serv, port, ctx); goto _dtor_and_return; } else { @@ -122,20 +159,17 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { ctx->upgrade = 1; } } else { - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); - if (fci_cache == nullptr) { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest); + if (cb == nullptr) { swoole_websocket_onRequest(ctx); goto _dtor_and_return; } } - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { - php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); -#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET - ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); -#endif - ctx->close(ctx); + ctx->private_data_2 = cb; + if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { + return SW_OK; } + http_server_process_request(serv, cb, ctx); } while (0); _dtor_and_return: @@ -145,34 +179,67 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { return SW_OK; } +void php_swoole_http_server_onClose(Server *serv, DataHead *info) { + client_ips.erase(info->fd); + php_swoole_server_onClose(serv, info); +} + void php_swoole_http_server_minit(int module_number) { - SW_INIT_CLASS_ENTRY_EX( - swoole_http_server, "Swoole\\Http\\Server", "swoole_http_server", nullptr, nullptr, swoole_server); + SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", nullptr, nullptr, swoole_server); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server); SW_SET_CLASS_CLONEABLE(swoole_http_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny); } +void php_swoole_http_server_rinit() { + // for is_uploaded_file and move_uploaded_file + if (!SG(rfc1867_uploaded_files)) { + ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); + zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0); + } +} + +void php_swoole_http_server_rshutdown() { + if (SG(rfc1867_uploaded_files)) { + destroy_uploaded_files_hash(); + SG(rfc1867_uploaded_files) = nullptr; + } + + client_ips.clear(); + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->end_ = 1; + ctx->onAfterResponse = nullptr; + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + } +} + HttpContext *swoole_http_context_new(SessionId fd) { HttpContext *ctx = new HttpContext(); zval *zrequest_object = &ctx->request._zobject; ctx->request.zobject = zrequest_object; - object_init_ex(zrequest_object, swoole_http_request_ce); + ZVAL_OBJ(zrequest_object, swoole_http_request_ce->create_object(swoole_http_request_ce)); php_swoole_http_request_set_context(zrequest_object, ctx); zval *zresponse_object = &ctx->response._zobject; ctx->response.zobject = zresponse_object; - object_init_ex(zresponse_object, swoole_http_response_ce); + ZVAL_OBJ(zresponse_object, swoole_http_response_ce->create_object(swoole_http_response_ce)); php_swoole_http_response_set_context(zresponse_object, ctx); - zend_update_property_long(swoole_http_request_ce, SW_Z8_OBJ_P(zrequest_object), ZEND_STRL("fd"), fd); - zend_update_property_long(swoole_http_response_ce, SW_Z8_OBJ_P(zresponse_object), ZEND_STRL("fd"), fd); + http_server_set_object_fd_property(SW_Z8_OBJ_P(zrequest_object), swoole_http_request_ce, fd); + http_server_set_object_fd_property(SW_Z8_OBJ_P(zresponse_object), swoole_http_response_ce, fd); + swoole_http_init_and_read_property(swoole_http_request_ce, + zrequest_object, + &ctx->request.zserver, + SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER), + HT_MIN_SIZE << 1); swoole_http_init_and_read_property( - swoole_http_request_ce, zrequest_object, &ctx->request.zserver, ZEND_STRL("server")); - swoole_http_init_and_read_property( - swoole_http_request_ce, zrequest_object, &ctx->request.zheader, ZEND_STRL("header")); + swoole_http_request_ce, zrequest_object, &ctx->request.zheader, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER)); + ctx->fd = fd; return ctx; @@ -186,6 +253,7 @@ void HttpContext::init(Server *serv) { enable_compression = serv->http_compression; compression_level = serv->http_compression_level; compression_min_length = serv->compression_min_length; + compression_types = serv->http_compression_types; #endif upload_tmp_dir = serv->upload_tmp_dir; bind(serv); @@ -205,6 +273,8 @@ void HttpContext::copy(HttpContext *ctx) { #ifdef SW_HAVE_COMPRESSION enable_compression = ctx->enable_compression; compression_level = ctx->compression_level; + compression_min_length = ctx->compression_min_length; + compression_types = ctx->compression_types; #endif co_socket = ctx->co_socket; private_data = ctx->private_data; @@ -212,6 +282,32 @@ void HttpContext::copy(HttpContext *ctx) { send = ctx->send; sendfile = ctx->sendfile; close = ctx->close; + onBeforeRequest = ctx->onBeforeRequest; + onAfterResponse = ctx->onAfterResponse; +} + +bool HttpContext::is_available() { + if (!response.zobject) { + return false; + } + if (co_socket) { + zval rv; + zval *zconn = zend_read_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), 1, &rv); + if (!zconn || ZVAL_IS_NULL(zconn)) { + return false; + } + if (php_swoole_socket_is_closed(zconn)) { + return false; + } + } else { + Server *serv = (Server *) private_data; + Connection *conn = serv->get_connection_by_session_id(fd); + if (!conn || conn->closed || conn->peer_closed) { + return false; + } + } + return true; } void HttpContext::free() { @@ -219,11 +315,9 @@ void HttpContext::free() { if (request.zobject || response.zobject) { return; } -#ifdef SW_USE_HTTP2 if (stream) { return; } -#endif HttpRequest *req = &request; HttpResponse *res = &response; @@ -236,31 +330,30 @@ void HttpContext::free() { if (req->chunked_body) { delete req->chunked_body; } -#ifdef SW_USE_HTTP2 if (req->h2_data_buffer) { delete req->h2_data_buffer; } -#endif if (res->reason) { efree(res->reason); } - delete this; -} - -void php_swoole_http_server_init_global_variant() { - swoole_http_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); - swoole_http_form_data_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); - // for is_uploaded_file and move_uploaded_file - if (!SG(rfc1867_uploaded_files)) { - ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); - zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0); + if (mt_parser) { + multipart_parser_free(mt_parser); + mt_parser = nullptr; + } + if (form_data_buffer) { + delete form_data_buffer; + form_data_buffer = nullptr; + } + if (write_buffer) { + delete write_buffer; } + delete this; } HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { HttpContext *ctx = php_swoole_http_request_get_context(zobject); if (!ctx) { - php_swoole_fatal_error(E_WARNING, "http request is unavailable (maybe it has been ended)"); + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); } return ctx; } @@ -268,7 +361,7 @@ HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { HttpContext *php_swoole_http_response_get_and_check_context(zval *zobject) { HttpContext *ctx = php_swoole_http_response_get_context(zobject); if (!ctx || (ctx->end_ || ctx->detached)) { - php_swoole_fatal_error(E_WARNING, "http response is unavailable (maybe it has been ended or detached)"); + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); return nullptr; } return ctx; @@ -281,6 +374,7 @@ bool http_context_send_data(HttpContext *ctx, const char *data, size_t length) { zval yield_data, return_value; ZVAL_STRINGL(&yield_data, data, length); php_swoole_server_send_yield(serv, ctx->fd, &yield_data, &return_value); + zval_ptr_dtor(&yield_data); return Z_BVAL_P(&return_value); } return retval; @@ -295,3 +389,67 @@ static bool http_context_disconnect(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; return serv->close(ctx->fd, 0); } + +bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { + ctx->onBeforeRequest = nullptr; + ctx->onAfterResponse = swoole_http_server_onAfterResponse; + Server *serv = (Server *) ctx->private_data; + if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) { + return false; + } + + auto worker = sw_worker(); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); + sw_atomic_add_fetch(&serv->gs->concurrency, 1); + worker->concurrency++; + if (worker->concurrency > serv->worker_max_concurrency) { + swoole_trace_log(SW_TRACE_COROUTINE, + "exceed worker_max_concurrency[%u] limit, request[%p] queued", + serv->worker_max_concurrency, + ctx); + queued_http_contexts.push(ctx); + return false; + } + + return true; +} + +void swoole_http_server_onAfterResponse(HttpContext *ctx) { + ctx->onAfterResponse = nullptr; + Server *serv = (Server *) ctx->private_data; + if (sw_unlikely(!sw_server() || !sw_worker())) { + return; + } + + if (sw_unlikely(sw_worker()->is_shutdown())) { + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); + ctx->close(ctx); + } + return; + } + + auto worker = sw_worker(); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); + sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + worker->concurrency--; + + if (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", worker->concurrency, ctx, ctx->request.zobject); + queued_http_contexts.pop(); + swoole_event_defer( + [](void *private_data) { + HttpContext *ctx = (HttpContext *) private_data; + Server *serv = (Server *) ctx->private_data; + zend::Callable *cb = (zend::Callable *) ctx->private_data_2; + swoole_trace("[POP 2] ctx=%p, request=%p", ctx, ctx->request.zobject); + http_server_process_request(serv, cb, ctx); + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + }, + ctx); + } +} diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 6d8cc3b9afc..2f3a18fe546 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,6 +19,11 @@ #include #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_server_coro_arginfo.h" +END_EXTERN_C() + +using swoole::Coroutine; using swoole::microtime; using swoole::PHPCoroutine; using swoole::Server; @@ -30,11 +35,9 @@ using HttpRequest = swoole::http::Request; using HttpResponse = swoole::http::Response; using HttpContext = swoole::http::Context; -#ifdef SW_USE_HTTP2 namespace http2 = swoole::http2; using Http2Stream = http2::Stream; using Http2Session = http2::Session; -#endif static zend_class_entry *swoole_http_server_coro_ce; static zend_object_handlers swoole_http_server_coro_handlers; @@ -43,47 +46,48 @@ static bool http_context_send_data(HttpContext *ctx, const char *data, size_t le static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); static bool http_context_disconnect(HttpContext *ctx); -#ifdef SW_USE_HTTP2 static void http2_server_onRequest(Http2Session *session, Http2Stream *stream); -#endif -class http_server { +namespace swoole { +namespace coroutine { + +class HttpServer { public: Socket *socket; - zend_fcall_info_cache *default_handler; - std::map handlers; - zval zcallbacks; + zend::Callable *default_handler; + std::unordered_map handlers; bool running; - std::list clients; + zval zclients; /* options */ - bool http_parse_cookie : 1; - bool http_parse_post : 1; - bool http_parse_files : 1; + bool parse_cookie; + bool parse_post; + bool parse_files; #ifdef SW_HAVE_COMPRESSION - bool http_compression : 1; + bool compression; #endif #ifdef SW_HAVE_ZLIB - bool websocket_compression : 1; + bool websocket_compression; #endif char *upload_tmp_dir; #ifdef SW_HAVE_COMPRESSION - uint8_t http_compression_level; + uint8_t compression_level; uint32_t compression_min_length; + std::shared_ptr> compression_types = nullptr; #endif - http_server(enum swSocketType type) { + HttpServer(enum swSocketType type) { socket = new Socket(type); default_handler = nullptr; - array_init(&zcallbacks); + array_init(&zclients); running = true; - http_parse_cookie = true; - http_parse_post = true; - http_parse_files = false; + parse_cookie = true; + parse_post = true; + parse_files = false; #ifdef SW_HAVE_COMPRESSION - http_compression = true; - http_compression_level = SW_Z_BEST_SPEED; + compression = true; + compression_level = SW_Z_BEST_SPEED; compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; #endif #ifdef SW_HAVE_ZLIB @@ -92,26 +96,38 @@ class http_server { upload_tmp_dir = sw_strdup("/tmp"); } - ~http_server() { + ~HttpServer() { sw_free(upload_tmp_dir); + zval_ptr_dtor(&zclients); + for (auto i = handlers.begin(); i != handlers.end(); i++) { + sw_callable_free(i->second); + } + delete socket; } - void set_handler(std::string pattern, zval *zcallback, const zend_fcall_info_cache *fci_cache) { - handlers[pattern] = *fci_cache; + bool set_handler(std::string pattern, zval *zfn) { + auto cb = sw_callable_create(zfn); + if (!cb) { + return false; + } + if (handlers.find(pattern) != handlers.end()) { + sw_callable_free(handlers[pattern]); + } + handlers[pattern] = cb; if (pattern == "/") { - default_handler = &handlers[pattern]; + default_handler = cb; } - Z_ADDREF_P(zcallback); - add_assoc_zval_ex(&zcallbacks, pattern.c_str(), pattern.length(), zcallback); + return true; } - zend_fcall_info_cache *get_handler(HttpContext *ctx) { + zend::Callable *get_handler(HttpContext *ctx) { for (auto i = handlers.begin(); i != handlers.end(); i++) { - if (&i->second == default_handler) { + if (i->second == default_handler) { continue; } - if (swoole_strcasect(ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { - return &i->second; + if (swoole_str_istarts_with( + ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { + return i->second; } } return default_handler; @@ -119,13 +135,14 @@ class http_server { HttpContext *create_context(Socket *conn, zval *zconn) { HttpContext *ctx = swoole_http_context_new(conn->get_fd()); - ctx->parse_body = http_parse_post; - ctx->parse_cookie = http_parse_cookie; - ctx->parse_files = http_parse_files; + ctx->parse_body = parse_post; + ctx->parse_cookie = parse_cookie; + ctx->parse_files = parse_files; #ifdef SW_HAVE_COMPRESSION - ctx->enable_compression = http_compression; - ctx->compression_level = http_compression_level; + ctx->enable_compression = compression; + ctx->compression_level = compression_level; ctx->compression_min_length = compression_min_length; + ctx->compression_types = compression_types; #endif #ifdef SW_HAVE_ZLIB ctx->websocket_compression = websocket_compression; @@ -138,12 +155,12 @@ class http_server { parser->data = ctx; swoole_http_parser_init(parser, PHP_HTTP_REQUEST); - zend_update_property(swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), ZEND_STRL("socket"), zconn); + zend_update_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zconn); return ctx; } -#ifdef SW_USE_HTTP2 void recv_http2_frame(HttpContext *ctx) { Socket *sock = (Socket *) ctx->private_data; http2::send_setting_frame(&sock->protocol, sock->get_socket()); @@ -176,13 +193,16 @@ class http_server { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } -#endif }; +}; // namespace coroutine +}; // namespace swoole + +using swoole::coroutine::HttpServer; -typedef struct { - http_server *server; +struct HttpServerObject { + HttpServer *server; zend_object std; -} http_server_coro_t; +}; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_http_server_coro, __construct); @@ -191,57 +211,34 @@ static PHP_METHOD(swoole_http_server_coro, handle); static PHP_METHOD(swoole_http_server_coro, start); static PHP_METHOD(swoole_http_server_coro, shutdown); static PHP_METHOD(swoole_http_server_coro, onAccept); -static PHP_METHOD(swoole_http_server_coro, __destruct); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, ssl) - ZEND_ARG_INFO(0, reuse_port) -ZEND_END_ARG_INFO() - - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_handle, 0, 0, 2) - ZEND_ARG_INFO(0, pattern) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_http_server_coro_methods[] = { - PHP_ME(swoole_http_server_coro, __construct, arginfo_swoole_http_server_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, set, arginfo_swoole_http_server_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, handle, arginfo_swoole_http_server_coro_handle, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, onAccept, arginfo_swoole_void, ZEND_ACC_PRIVATE) - PHP_ME(swoole_http_server_coro, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, shutdown, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Server___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, set, arginfo_class_Swoole_Coroutine_Http_Server_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, handle, arginfo_class_Swoole_Coroutine_Http_Server_handle, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, onAccept, arginfo_class_Swoole_Coroutine_Http_Server_onAccept, ZEND_ACC_PRIVATE) + PHP_ME(swoole_http_server_coro, start, arginfo_class_Swoole_Coroutine_Http_Server_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, shutdown, arginfo_class_Swoole_Coroutine_Http_Server_shutdown, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on static zend_object *php_swoole_http_server_coro_create_object(zend_class_entry *ce) { - http_server_coro_t *hsc = (http_server_coro_t *) zend_object_alloc(sizeof(http_server_coro_t), ce); + HttpServerObject *hsc = (HttpServerObject *) zend_object_alloc(sizeof(HttpServerObject), ce); zend_object_std_init(&hsc->std, ce); object_properties_init(&hsc->std, ce); hsc->std.handlers = &swoole_http_server_coro_handlers; return &hsc->std; } -static sw_inline http_server_coro_t *php_swoole_http_server_coro_fetch_object(zend_object *obj) { - return (http_server_coro_t *) ((char *) obj - swoole_http_server_coro_handlers.offset); +static sw_inline HttpServerObject *php_swoole_http_server_coro_fetch_object(zend_object *obj) { + return (HttpServerObject *) ((char *) obj - swoole_http_server_coro_handlers.offset); } -static sw_inline http_server *http_server_get_object(zend_object *obj) { +static sw_inline HttpServer *http_server_get_object(zend_object *obj) { return php_swoole_http_server_coro_fetch_object(obj)->server; } @@ -266,10 +263,9 @@ static bool http_context_disconnect(HttpContext *ctx) { } static void php_swoole_http_server_coro_free_object(zend_object *object) { - http_server_coro_t *hsc = php_swoole_http_server_coro_fetch_object(object); + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(object); if (hsc->server) { - http_server *hs = hsc->server; - zval_ptr_dtor(&hs->zcallbacks); + HttpServer *hs = hsc->server; delete hs; } zend_object_std_dtor(&hsc->std); @@ -301,7 +297,6 @@ void HttpContext::bind(Socket *sock) { void php_swoole_http_server_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_http_server_coro, "Swoole\\Coroutine\\Http\\Server", - nullptr, "Co\\Http\\Server", swoole_http_server_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server_coro); @@ -311,15 +306,9 @@ void php_swoole_http_server_coro_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_server_coro, php_swoole_http_server_coro_create_object, php_swoole_http_server_coro_free_object, - http_server_coro_t, + HttpServerObject, std); swoole_http_server_coro_ce->ce_flags |= ZEND_ACC_FINAL; - swoole_http_server_coro_handlers.get_gc = [](sw_zend7_object *object, zval **gc_data, int *gc_count) { - http_server_coro_t *hs = php_swoole_http_server_coro_fetch_object(SW_Z7_OBJ_P(object)); - *gc_data = &hs->server->zcallbacks; - *gc_count = 1; - return zend_std_get_properties(object); - }; zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); @@ -354,9 +343,9 @@ static PHP_METHOD(swoole_http_server_coro, __construct) { RETURN_FALSE; } - http_server_coro_t *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); std::string host_str(host, l_host); - hsc->server = new http_server(swoole::network::Socket::convert_to_type(host_str)); + hsc->server = new HttpServer(swoole::network::Socket::convert_to_type(host_str)); Socket *sock = hsc->server->socket; if (reuse_port) { @@ -398,18 +387,17 @@ static PHP_METHOD(swoole_http_server_coro, __construct) { static PHP_METHOD(swoole_http_server_coro, handle) { char *pattern; size_t pattern_len; + zval *zfn; - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STRING(pattern, pattern_len) - Z_PARAM_FUNC(fci, fci_cache) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END(); std::string key(pattern, pattern_len); - hs->set_handler(key, ZEND_CALL_ARG(execute_data, 2), &fci_cache); + RETURN_BOOL(hs->set_handler(key, zfn)); } static PHP_METHOD(swoole_http_server_coro, set) { @@ -430,16 +418,15 @@ static PHP_METHOD(swoole_http_server_coro, set) { } static PHP_METHOD(swoole_http_server_coro, start) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); Socket *sock = hs->socket; /* get callback fci cache */ char *func_name = nullptr; zend_fcall_info_cache fci_cache; - zval zcallback; - ZVAL_STRING(&zcallback, "onAccept"); + zend::Variable zcallback("onAccept"); if (!sw_zend_is_callable_at_frame( - &zcallback, ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) { + zcallback.ptr(), ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) { php_swoole_fatal_error(E_CORE_ERROR, "function '%s' is not callable", func_name); return; } @@ -453,22 +440,23 @@ static PHP_METHOD(swoole_http_server_coro, start) { zval *ztmp; // parse cookie header if (php_swoole_array_get_value(vht, "http_parse_cookie", ztmp)) { - hs->http_parse_cookie = zval_is_true(ztmp); + hs->parse_cookie = zval_is_true(ztmp); } // parse x-www-form-urlencoded form data if (php_swoole_array_get_value(vht, "http_parse_post", ztmp)) { - hs->http_parse_post = zval_is_true(ztmp); + hs->parse_post = zval_is_true(ztmp); } // parse multipart/form-data file uploads if (php_swoole_array_get_value(vht, "http_parse_files", ztmp)) { - hs->http_parse_files = zval_is_true(ztmp); + hs->parse_files = zval_is_true(ztmp); } #ifdef SW_HAVE_COMPRESSION // http content compression if (php_swoole_array_get_value(vht, "http_compression", ztmp)) { - hs->http_compression = zval_is_true(ztmp); + hs->compression = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || + php_swoole_array_get_value(vht, "compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) { zend_long level = zval_get_long(ztmp); if (level > UINT8_MAX) { @@ -476,11 +464,28 @@ static PHP_METHOD(swoole_http_server_coro, start) { } else if (level < 0) { level = 0; } - hs->http_compression_level = level; + hs->compression_level = level; } - if (php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { + if (php_swoole_array_get_value(vht, "http_compression_min_length", ztmp) || + php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { hs->compression_min_length = zval_get_long(ztmp); } + if (php_swoole_array_get_value(vht, "http_compression_types", ztmp) || + php_swoole_array_get_value(vht, "compression_types", ztmp)) { + hs->compression_types = std::make_shared>(); + if (ZVAL_IS_ARRAY(ztmp)) { + zval *ztype; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype) + zend::String type(ztype); + if (type.len() > 0) { + hs->compression_types->emplace(type.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "http_compression_types must be array"); + RETURN_FALSE; + } + } #endif #ifdef SW_HAVE_ZLIB if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) { @@ -500,14 +505,14 @@ static PHP_METHOD(swoole_http_server_coro, start) { hs->upload_tmp_dir = str_v.dup(); } - php_swoole_http_server_init_global_variant(); + hs->running = true; while (hs->running) { auto conn = sock->accept(); if (conn) { zval zsocket; php_swoole_init_socket_object(&zsocket, conn); - long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket); + long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket, zcallback.ptr()); zval_dtor(&zsocket); if (cid < 0) { goto _wait_1s; @@ -532,39 +537,38 @@ static PHP_METHOD(swoole_http_server_coro, start) { } } - zval_dtor(&zcallback); - RETURN_TRUE; } -static PHP_METHOD(swoole_http_server_coro, __destruct) {} - static PHP_METHOD(swoole_http_server_coro, onAccept) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); zval *zconn; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) Z_PARAM_OBJECT(zconn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Coroutine *co = Coroutine::get_current(); Socket *sock = php_swoole_get_socket(zconn); sock->set_buffer_allocator(sw_zend_string_allocator()); String *buffer = sock->get_read_buffer(); HttpContext *ctx = nullptr; bool header_completed = false; off_t header_crlf_offset = 0; - - hs->clients.push_front(sock); - auto client_iterator = hs->clients.begin(); + size_t total_length; #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { - goto _handshake_failed; + RETURN_FALSE; } #endif + Z_TRY_ADDREF_P(zconn); + zend_hash_index_add(Z_ARRVAL_P(&hs->zclients), co->get_cid(), zconn); + zend::Variable remote_addr = zend::Variable(sock->get_ip()); while (true) { _recv_request : { + sock->get_socket()->recv_wait = 1; ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { break; @@ -578,49 +582,67 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } if (!header_completed) { - if (swoole_strnpos( - buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")) < 0) { + ssize_t pos = swoole_strnpos( + buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")); + if (pos < 0) { if (buffer->length == buffer->size) { ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; break; } header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0; continue; - } else { - header_completed = true; - header_crlf_offset = 0; } - } - - size_t parsed_n = ctx->parse(buffer->str + buffer->offset, buffer->length - buffer->offset); - buffer->offset += parsed_n; - swoole_trace_log(SW_TRACE_CO_HTTP_SERVER, - "parsed_n=%ld, length=%ld, offset=%ld, completed=%d", - parsed_n, - buffer->length, - buffer->offset, - ctx->completed); + size_t header_length = header_crlf_offset + pos; + header_completed = true; + header_crlf_offset = 0; - if (!ctx->completed) { - if (ctx->parser.state == s_dead) { + // The HTTP header must be parsed first + // Header contains CRLFx2 + header_length += 4; + size_t parsed_n = ctx->parse(buffer->str, header_length); + if (parsed_n != header_length) { ctx->response.status = SW_HTTP_BAD_REQUEST; break; } - if (ctx->parser.content_length > 0 && ctx->parser.content_length > sock->protocol.package_max_length) { + buffer->offset += header_length; + total_length = header_length + ctx->get_content_length(); + if (ctx->get_content_length() > 0 && total_length > sock->protocol.package_max_length) { ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; break; } - if (buffer->length == buffer->size) { - if (!buffer->extend()) { - ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; - break; - } + if (total_length > buffer->size && !buffer->extend(total_length)) { + ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; + break; + } + } + + if (!ctx->completed) { + // Make sure the complete request package is received + if (ctx->recv_chunked && memcmp(buffer->str + buffer->length - (sizeof(SW_HTTP_CHUNK_EOF) - 1), + SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) { + goto _recv_request; + } + if (buffer->length < total_length) { + goto _recv_request; + } + + size_t parsed_n = ctx->parse(buffer->str + buffer->offset, buffer->length - buffer->offset); + buffer->offset += parsed_n; + + swoole_trace_log(SW_TRACE_CO_HTTP_SERVER, + "parsed_n=%zu, length=%zu, offset=%jd, completed=%u", + parsed_n, + buffer->length, + (intmax_t) buffer->offset, + ctx->completed); + + if (ctx->parser.state == s_dead) { + ctx->response.status = SW_HTTP_BAD_REQUEST; + break; } - continue; } -#ifdef SW_USE_HTTP2 if (ctx->parser.method == PHP_HTTP_NOT_IMPLEMENTED && buffer->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) && memcmp(buffer->str, SW_HTTP2_PRI_STRING, sizeof(SW_HTTP2_PRI_STRING) - 1) == 0) { buffer->offset = (sizeof(SW_HTTP2_PRI_STRING) - 1); @@ -629,22 +651,24 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { ctx = nullptr; break; } -#endif - size_t total_length = buffer->offset; zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); zval *zserver = ctx->request.zserver; - add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); - add_assoc_long(zserver, "remote_port", (zend_long) sock->get_port()); - add_assoc_string(zserver, "remote_addr", (char *) sock->get_ip()); - - zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); + http_server_add_server_array( + Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) hs->socket->get_bind_port()); + http_server_add_server_array( + Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) sock->get_port()); + http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), remote_addr.ptr()); + remote_addr.add_ref(); + + zend::Callable *cb = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; bool keep_alive = swoole_http_should_keep_alive(&ctx->parser) && !ctx->websocket; + sock->get_socket()->recv_wait = 0; - if (fci_cache) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, 0))) { + if (cb) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, 0))) { php_swoole_error(E_WARNING, "handler error"); } } else { @@ -655,7 +679,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(&args[1]); ctx = nullptr; - if (!hs->running || !keep_alive) { + if (!hs->running || !keep_alive || php_swoole_socket_is_closed(zconn)) { break; } else { header_completed = false; @@ -671,31 +695,29 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - -#ifdef SW_USE_OPENSSL -_handshake_failed: -#endif - /* notice: do not erase the element when server is shutting down */ - if (hs->running) { - hs->clients.erase(client_iterator); - } + zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), co->get_cid()); } static PHP_METHOD(swoole_http_server_coro, shutdown) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); - /* accept has been canceled, we only need to traverse once */ - for (auto client : hs->clients) { - client->close(); + + zend_ulong index; + zval *zconn; + ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(&hs->zclients), index, zconn) { + Socket *sock = php_swoole_get_socket(zconn); + if (sock->get_socket()->recv_wait) { + sock->cancel(SW_EVENT_READ); + zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), index); + } } - hs->clients.clear(); + ZEND_HASH_FOREACH_END(); } -#ifdef SW_USE_HTTP2 static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { HttpContext *ctx = stream->ctx; - http_server *hs = (http_server *) session->private_data; + HttpServer *hs = (HttpServer *) session->private_data; Socket *sock = (Socket *) ctx->private_data; zval *zserver = ctx->request.zserver; @@ -706,11 +728,11 @@ static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { add_assoc_string(zserver, "remote_addr", (char *) sock->get_ip()); add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); - zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); + zend::Callable *cb = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; - if (fci_cache) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, true))) { + if (cb) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, true))) { stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR); php_swoole_error(E_WARNING, "%s->onRequest[v2] handler error", ZSTR_VAL(swoole_http_server_ce->name)); } @@ -721,4 +743,3 @@ static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[1]); } -#endif diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 075f624c899..fdd6806d379 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,6 +18,10 @@ #include "swoole_memory.h" #include "swoole_lock.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_lock_arginfo.h" +END_EXTERN_C() + using swoole::Lock; using swoole::Mutex; #ifdef HAVE_SPINLOCK @@ -45,8 +49,8 @@ static Lock *php_swoole_lock_get_ptr(zval *zobject) { static Lock *php_swoole_lock_get_and_check_ptr(zval *zobject) { Lock *lock = php_swoole_lock_get_ptr(zobject); - if (!lock) { - php_swoole_fatal_error(E_ERROR, "you must call Lock constructor first"); + if (UNEXPECTED(!lock)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return lock; } @@ -56,10 +60,6 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { } static void php_swoole_lock_free_object(zend_object *object) { - LockObject *o = php_swoole_lock_fetch_object(object); - if (o->lock) { - delete o->lock; - } zend_object_std_dtor(object); } @@ -80,48 +80,32 @@ static PHP_METHOD(swoole_lock, trylock); static PHP_METHOD(swoole_lock, lock_read); static PHP_METHOD(swoole_lock, trylock_read); static PHP_METHOD(swoole_lock, unlock); -static PHP_METHOD(swoole_lock, destroy); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_lock_construct, 0, 0, 0) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_lock_lockwait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_lock_methods[] = { - PHP_ME(swoole_lock, __construct, arginfo_swoole_lock_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lockwait, arginfo_swoole_lock_lockwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, trylock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, trylock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, unlock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, destroy, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, __construct, arginfo_class_Swoole_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, __destruct, arginfo_class_Swoole_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lock, arginfo_class_Swoole_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lockwait, arginfo_class_Swoole_Lock_locakwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, trylock, arginfo_class_Swoole_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lock_read, arginfo_class_Swoole_Lock_lock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, trylock_read, arginfo_class_Swoole_Lock_trylock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, unlock, arginfo_class_Swoole_Lock_unlock, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_lock_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", "swoole_lock", nullptr, swoole_lock_methods); + SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", nullptr, swoole_lock_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_lock); SW_SET_CLASS_CLONEABLE(swoole_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_lock, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_lock, php_swoole_lock_create_object, php_swoole_lock_free_object, LockObject, std); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("FILELOCK"), Lock::FILE_LOCK); zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SEM"), Lock::SEM); #ifdef HAVE_RWLOCK zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); #endif @@ -130,9 +114,7 @@ void php_swoole_lock_minit(int module_number) { #endif zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_FILELOCK", Lock::FILE_LOCK); SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SEM", Lock::SEM); #ifdef HAVE_RWLOCK SW_REGISTER_LONG_CONSTANT("SWOOLE_RWLOCK", Lock::RW_LOCK); #endif @@ -144,7 +126,8 @@ void php_swoole_lock_minit(int module_number) { static PHP_METHOD(swoole_lock, __construct) { Lock *lock = php_swoole_lock_get_ptr(ZEND_THIS); if (lock != nullptr) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zend_long type = Lock::MUTEX; @@ -156,12 +139,6 @@ static PHP_METHOD(swoole_lock, __construct) { } switch (type) { - case Lock::FILE_LOCK: - case Lock::SEM: - zend_throw_exception( - swoole_exception_ce, "FileLock and SemLock is no longer supported, please use mutex lock", errno); - RETURN_FALSE; - break; #ifdef HAVE_SPINLOCK case Lock::SPIN_LOCK: lock = new SpinLock(1); @@ -173,9 +150,12 @@ static PHP_METHOD(swoole_lock, __construct) { break; #endif case Lock::MUTEX: - default: lock = new Mutex(Mutex::PROCESS_SHARED); break; + default: + zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type); + RETURN_FALSE; + break; } php_swoole_lock_set_ptr(ZEND_THIS, lock); RETURN_TRUE; @@ -191,9 +171,11 @@ static PHP_METHOD(swoole_lock, lock) { static PHP_METHOD(swoole_lock, lockwait) { double timeout = 1.0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); if (lock->get_type() != Lock::MUTEX) { zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); @@ -226,9 +208,3 @@ static PHP_METHOD(swoole_lock, lock_read) { Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->lock_rd()); } - -static PHP_METHOD(swoole_lock, destroy) { - Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - delete lock; - php_swoole_lock_set_ptr(ZEND_THIS, nullptr); -} diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc deleted file mode 100644 index 1b6021e9577..00000000000 --- a/ext-src/swoole_mysql_coro.cc +++ /dev/null @@ -1,2207 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2018 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "php_swoole_mysql_proto.h" - -#include "swoole_string.h" - -// see mysqlnd 'L64' macro redefined -#undef L64 - -SW_EXTERN_C_BEGIN -#include "ext/hash/php_hash.h" -#include "ext/hash/php_hash_sha.h" -#include "ext/standard/php_math.h" -#ifdef SW_USE_MYSQLND -#include "ext/mysqlnd/mysqlnd.h" -#include "ext/mysqlnd/mysqlnd_charset.h" -#endif -SW_EXTERN_C_END - -#include - -/* keep same with pdo and mysqli */ -#define MYSQLND_UNKNOWN_SQLSTATE "HY000" -#define MYSQLND_SERVER_GONE "MySQL server has gone away" -#define MYSQLND_CR_UNKNOWN_ERROR 2000 -#define MYSQLND_CR_CONNECTION_ERROR 2002 -#define MYSQLND_CR_SERVER_GONE_ERROR 2006 -#define MYSQLND_CR_OUT_OF_MEMORY 2008 -#define MYSQLND_CR_SERVER_LOST 2013 -#define MYSQLND_CR_COMMANDS_OUT_OF_SYNC 2014 -#define MYSQLND_CR_CANT_FIND_CHARSET 2019 -#define MYSQLND_CR_MALFORMED_PACKET 2027 -#define MYSQLND_CR_NOT_IMPLEMENTED 2054 -#define MYSQLND_CR_NO_PREPARE_STMT 2030 -#define MYSQLND_CR_PARAMS_NOT_BOUND 2031 -#define MYSQLND_CR_INVALID_PARAMETER_NO 2034 -#define MYSQLND_CR_INVALID_BUFFER_USE 2035 - -using swoole::coroutine::Socket; -using namespace swoole; - -namespace swoole { -class mysql_statement; -class mysql_client { - public: - /* session related {{{ */ - Socket *socket = nullptr; - Socket::timeout_controller *tc = nullptr; - - enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; - bool quit = false; - mysql::result_info result; - - std::unordered_map statements; - mysql_statement *statement = nullptr; - /* }}} */ - - std::string host = SW_MYSQL_DEFAULT_HOST; - uint16_t port = SW_MYSQL_DEFAULT_PORT; - bool ssl = false; - - std::string user = "root"; - std::string password = "root"; - std::string database = "test"; - char charset = SW_MYSQL_DEFAULT_CHARSET; - - double connect_timeout = network::Socket::default_connect_timeout; - bool strict_type = false; - - inline int get_error_code() { - return error_code; - } - - inline const char *get_error_msg() { - return error_msg.c_str(); - } - - inline void non_sql_error(int code, const char *msg) { - error_code = code; - error_msg = std_string::format("SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, msg); - } - - template - inline void non_sql_error(int code, const char *format, Args... args) { - error_code = code; - error_msg = std_string::format( - "SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, std_string::format(format, args...).c_str()); - } - - void io_error() { - if (state == SW_MYSQL_STATE_CLOSED) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, socket->errMsg); - } else { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, - MYSQLND_SERVER_GONE "%s%s", - socket->errCode ? " due to " : "", - socket->errCode ? socket->errMsg : ""); - } - /* don't send QUIT after IO error */ - quit = true; - close(); - } - - void proto_error(const char *data, const enum sw_mysql_packet_types expected_type) { - mysql::server_packet packet(data); - non_sql_error(MYSQLND_CR_MALFORMED_PACKET, - "Unexpected mysql packet length=%u, number=%u, type=%u, expected_type=%u", - packet.header.length, - packet.header.number, - (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE], - expected_type); - close(); - } - - void server_error(const char *data) { - mysql::err_packet err_packet(data); - error_code = err_packet.code; - error_msg = - std_string::format("SQLSTATE[%s] [%d] %s", err_packet.sql_state, err_packet.code, err_packet.msg.c_str()); - state = SW_MYSQL_STATE_IDLE; - } - - inline bool get_fetch_mode() { - return fetch_mode; - } - - inline bool set_fetch_mode(bool v) { - if (sw_unlikely(socket && v)) { - non_sql_error(ENOTSUP, "Can not use fetch mode after the connection is established"); - return false; - } - fetch_mode = v; - return true; - } - - inline bool get_defer() { - return defer; - } - - inline bool set_defer(bool v) { - // if (sw_unlikely(fetch_mode && v)) - // { - // non_sql_error(ENOTSUP, "Can not use defer mode when fetch mode is on"); - // return false; - // } - defer = v; - return true; - } - - void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_unlikely(!socket)) { - return; - } - // Notice: `timeout > 0` is wrong, maybe -1 - if (timeout != 0) { - SW_ASSERT(!tc); - tc = new Socket::timeout_controller(socket, timeout, type); - } - } - - inline bool has_timedout(enum Socket::TimeoutType type) { - return tc && tc->has_timedout(type); - } - - void del_timeout_controller() { - if (tc) { - delete tc; - tc = nullptr; - } - } - - bool connect(std::string host, uint16_t port, bool ssl); - - inline bool connect() { - return connect(host, port, ssl); - } - - inline bool is_connected() { - return socket && socket->is_connected(); - } - - inline int get_fd() { - return socket ? socket->get_fd() : -1; - } - - inline bool check_connection() { - if (sw_unlikely(!is_connected())) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, "%s or %s", strerror(ECONNRESET), strerror(ENOTCONN)); - return false; - } - return true; - } - - inline bool check_liveness() { - if (sw_unlikely(!check_connection())) { - return false; - } - if (sw_unlikely(!socket->check_liveness())) { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, MYSQLND_SERVER_GONE); - close(); - return false; - } - return true; - } - - inline bool is_writable() { - return is_connected() && !socket->has_bound(SW_EVENT_WRITE); - } - - bool is_available_for_new_request() { - if (sw_unlikely(state != SW_MYSQL_STATE_IDLE && state != SW_MYSQL_STATE_CLOSED)) { - if (socket) { - socket->check_bound_co(SW_EVENT_RDWR); - } - non_sql_error(EINPROGRESS, - "MySQL client is busy now on state#%d, " - "please use recv/fetchAll/nextResult to get all unread data " - "and wait for response then try again", - state); - return false; - } - if (sw_unlikely(!check_liveness())) { - return false; - } else { - /* without unread data */ - String *buffer = socket->get_read_buffer(); - SW_ASSERT(buffer->length == (size_t) buffer->offset); - buffer->clear(); - return true; - } - } - - const char *recv_packet(); - - inline const char *recv_none_error_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && mysql::server_packet::is_err(data))) { - server_error(data); - return nullptr; - } - return data; - } - - inline const char *recv_eof_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && !mysql::server_packet::is_eof(data))) { - proto_error(data, SW_MYSQL_PACKET_EOF); - return nullptr; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::eof_packet eof_packet(data); -#endif - return data; - } - - inline bool send_raw(const char *data, size_t length) { - if (sw_unlikely(!check_connection())) { - return false; - } else { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_WRITE))) { - io_error(); - return false; - } - if (sw_unlikely(socket->send_all(data, length) != (ssize_t) length)) { - io_error(); - return false; - } - return true; - } - } - - bool send_packet(mysql::client_packet *packet); - bool send_command(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - // just for internal - void send_command_without_check(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - - void query(zval *return_value, const char *statement, size_t statement_length); - void send_query_request(zval *return_value, const char *statement, size_t statement_length); - void recv_query_response(zval *return_value); - const char *handle_row_data_size(mysql::row_data *row_data, uint8_t size); - bool handle_row_data_lcb(mysql::row_data *row_data); - void handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field); - void handle_strict_type(zval *ztext, mysql::field_packet *field); - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - bool recv(); - - bool send_prepare_request(const char *statement, size_t statement_length); - mysql_statement *recv_prepare_response(); - - void close(); - - ~mysql_client() { - SW_ASSERT(statements.empty()); - close(); - } - - private: - int error_code = 0; - std::string error_msg = ""; - - /* unable to support both features at the same time, so we have to set them by method {{{ */ - bool fetch_mode = false; - bool defer = false; - /* }}} */ - - // recv data of specified length - const char *recv_length(size_t need_length, const bool try_to_recycle = false); - // usually mysql->connect = connect(TCP) + handshake - bool handshake(); -}; - -class mysql_statement { - public: - std::string statement; - mysql::statement info; - mysql::result_info result; - - mysql_statement(mysql_client *client, const char *statement, size_t statement_length) : client(client) { - this->statement = std::string(statement, statement_length); - } - - inline mysql_client *get_client() { - return client; - } - - inline int get_error_code() { - return sw_likely(client) ? client->get_error_code() : error_code; - } - - inline const char *get_error_msg() { - return sw_likely(client) ? client->get_error_msg() : error_msg.c_str(); - } - - inline bool is_available() { - if (sw_unlikely(!client)) { - error_code = ECONNRESET; - error_msg = "statement must to be recompiled after the connection is broken"; - return false; - } - return true; - } - - inline bool is_available_for_new_request() { - if (sw_unlikely(!is_available())) { - return false; - } - if (sw_unlikely(!client->is_available_for_new_request())) { - return false; - } - return true; - } - - inline void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_likely(client)) { - client->add_timeout_controller(timeout, type); - } - } - - inline void del_timeout_controller() { - if (sw_likely(client)) { - client->del_timeout_controller(); - } - } - - // [notify = false]: mysql_client actively close - inline void close(const bool notify = true) { - if (client) { - // if client point exists, socket is always available - if (notify) { - if (sw_likely(client->is_writable())) { - char id[4]; - sw_mysql_int4store(id, info.id); - client->send_command_without_check(SW_MYSQL_COM_STMT_CLOSE, id, sizeof(id)); - } - client->statements.erase(info.id); - } else { - error_code = client->get_error_code(); - error_msg = client->get_error_msg(); - } - client = nullptr; - } - } - - ~mysql_statement() { - close(); - } - - bool send_prepare_request(); - bool recv_prepare_response(); - - void execute(zval *return_value, zval *params); - void send_execute_request(zval *return_value, zval *params); - void recv_execute_response(zval *return_value); - - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - - private: - mysql_client *client = nullptr; - int error_code = 0; - std::string error_msg; -}; -} // namespace swoole - -using swoole::mysql_client; -using swoole::mysql_statement; - -static zend_class_entry *swoole_mysql_coro_ce; -static zend_object_handlers swoole_mysql_coro_handlers; - -static zend_class_entry *swoole_mysql_coro_exception_ce; -static zend_object_handlers swoole_mysql_coro_exception_handlers; - -static zend_class_entry *swoole_mysql_coro_statement_ce; -static zend_object_handlers swoole_mysql_coro_statement_handlers; - -struct mysql_coro_t { - mysql_client *client; - zend_object std; -}; - -struct mysql_coro_statement_t { - mysql_statement *statement; - zend_object *zclient; - zend_object std; -}; - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_mysql_coro, __construct); -static PHP_METHOD(swoole_mysql_coro, __destruct); -static PHP_METHOD(swoole_mysql_coro, connect); -static PHP_METHOD(swoole_mysql_coro, getDefer); -static PHP_METHOD(swoole_mysql_coro, setDefer); -static PHP_METHOD(swoole_mysql_coro, query); -static PHP_METHOD(swoole_mysql_coro, fetch); -static PHP_METHOD(swoole_mysql_coro, fetchAll); -static PHP_METHOD(swoole_mysql_coro, nextResult); -static PHP_METHOD(swoole_mysql_coro, prepare); -static PHP_METHOD(swoole_mysql_coro, recv); -static PHP_METHOD(swoole_mysql_coro, begin); -static PHP_METHOD(swoole_mysql_coro, commit); -static PHP_METHOD(swoole_mysql_coro, rollback); -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape); -#endif -static PHP_METHOD(swoole_mysql_coro, close); - -static PHP_METHOD(swoole_mysql_coro_statement, execute); -static PHP_METHOD(swoole_mysql_coro_statement, fetch); -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); -static PHP_METHOD(swoole_mysql_coro_statement, nextResult); -static PHP_METHOD(swoole_mysql_coro_statement, recv); -static PHP_METHOD(swoole_mysql_coro_statement, close); -SW_EXTERN_C_END - -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_optional_timeout, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_connect, 0, 0, 0) - ZEND_ARG_ARRAY_INFO(0, server_config, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_query, 0, 0, 1) - ZEND_ARG_INFO(0, sql) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_prepare, 0, 0, 1) - ZEND_ARG_INFO(0, query) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_setDefer, 0, 0, 0) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_MYSQLND -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_escape, 0, 0, 1) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0) - ZEND_ARG_INFO(0, params) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -static const zend_function_entry swoole_mysql_coro_methods[] = -{ - PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, setDefer, arginfo_swoole_mysql_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, connect, arginfo_swoole_mysql_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, query, arginfo_swoole_mysql_coro_query, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetch, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetchAll, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, nextResult, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, prepare, arginfo_swoole_mysql_coro_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, recv, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, begin, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, commit, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, rollback, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) -#ifdef SW_USE_MYSQLND - PHP_ME(swoole_mysql_coro, escape, arginfo_swoole_mysql_coro_escape, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_mysql_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_mysql_coro_statement_methods[] = -{ - PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, nextResult, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, recv, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -void php_swoole_sha256(const char *str, int len, unsigned char *digest) { - PHP_SHA256_CTX context; - PHP_SHA256Init(&context); - PHP_SHA256Update(&context, (unsigned char *) str, len); - PHP_SHA256Final(digest, &context); -} - -bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { - if (socket && (host != this->host || port != this->port || ssl != this->ssl)) { - close(); - } - if (!socket) { - if (host.compare(0, 6, "unix:/", 0, 6) == 0) { - host = host.substr(sizeof("unix:") - 1); - host.erase(0, host.find_first_not_of('/') - 1); - socket = new Socket(SW_SOCK_UNIX_STREAM); - } else if (host.find(':') != std::string::npos) { - socket = new Socket(SW_SOCK_TCP6); - } else { - socket = new Socket(SW_SOCK_TCP); - } - if (sw_unlikely(socket->get_fd() < 0)) { - php_swoole_fatal_error(E_WARNING, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); - delete socket; - socket = nullptr; - return false; - } - socket->set_zero_copy(true); -#ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } -#endif - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL); - if (!socket->connect(host, port)) { - io_error(); - return false; - } - this->host = host; - this->port = port; -#ifdef SW_USE_OPENSSL - this->ssl = ssl; -#endif - if (!handshake()) { - close(); - return false; - } - state = SW_MYSQL_STATE_IDLE; - quit = false; - del_timeout_controller(); - } - return true; -} - -const char *mysql_client::recv_length(size_t need_length, const bool try_to_recycle) { - if (sw_likely(check_connection())) { - ssize_t retval; - String *buffer = socket->get_read_buffer(); - off_t offset = buffer->offset; // save offset instead of buffer point (due to realloc) - size_t read_n = buffer->length - buffer->offset; // readable bytes - if (try_to_recycle && read_n == 0) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "mysql buffer will be recycled, length=%zu, offset=%jd", - buffer->length, - (intmax_t) offset); - buffer->clear(); - offset = 0; - } - while (read_n < need_length) { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_READ))) { - io_error(); - return nullptr; - } - if (sw_unlikely(buffer->length == buffer->size)) { - /* offset + need_length = new size (min) */ - if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, SwooleG.pagesize))) { - non_sql_error(MYSQLND_CR_OUT_OF_MEMORY, strerror(ENOMEM)); - return nullptr; - } else { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "mysql buffer extend to %zu", buffer->size); - } - } - retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); - if (sw_unlikely(retval <= 0)) { - io_error(); - return nullptr; - } - read_n += retval; - buffer->length += retval; - } - buffer->offset += need_length; - return buffer->str + offset; - } - return nullptr; -} - -const char *mysql_client::recv_packet() { - const char *p; - uint32_t length; - p = recv_length(SW_MYSQL_PACKET_HEADER_SIZE, true); - if (sw_unlikely(!p)) { - return nullptr; - } - length = mysql::packet::get_length(p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "recv packet length=%u, number=%u", length, mysql::packet::get_number(p)); - p = recv_length(length); - if (sw_unlikely(!p)) { - return nullptr; - } - /* Notice: why we do this? because buffer maybe reallocated when recv data */ - return p - SW_MYSQL_PACKET_HEADER_SIZE; -} - -bool mysql_client::send_packet(mysql::client_packet *packet) { - const char *data = packet->get_data(); - uint32_t length = SW_MYSQL_PACKET_HEADER_SIZE + packet->get_length(); - if (sw_likely(send_raw(data, length))) { - return true; - } - return false; -} - -bool mysql_client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { - if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= SwooleG.pagesize)) { - mysql::command_packet command_packet(command, sql, length); - return send_raw(command_packet.get_data(), command_packet.get_data_length()); - } else { - /* if the data is larger than page_size, copy memory to the kernel buffer multiple times is much faster */ - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE - 1), send_n = send_s, number = 0; - mysql::command_packet command_packet(command); - command_packet.set_header(1 + send_s, number++); - - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE + 1)) || - !send_raw(sql, send_s)) { - return false; - } - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - command_packet.set_header(send_s, number++); - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE)) || - !send_raw(sql + send_n, send_s)) { - return false; - } - send_n += send_s; - } - return true; - } -} - -void mysql_client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) { - mysql::command_packet command_packet(command, sql, length); - (void) (socket && socket->send(command_packet.get_data(), command_packet.get_data_length())); -} - -bool mysql_client::handshake() { - const char *data; - // recv greeting pakcet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::greeting_packet greeting_packet(data); - // generate login packet - do { - mysql::login_packet login_packet(&greeting_packet, user, password, database, charset); - if (sw_unlikely(!send_packet(&login_packet))) { - return false; - } - } while (0); - // recv auth switch request packet, 4 possible packet types - switch (mysql::server_packet::parse_type(data = recv_packet())) { - case SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST: { - mysql::auth_switch_request_packet request(data); - mysql::auth_switch_response_packet response(&request, password); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - break; - } - case SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST: { - mysql::auth_signature_request_packet request(data); - if (sw_unlikely(!request.is_vaild())) { - goto _proto_error; - } - if (sw_likely(!request.is_full_auth_required())) { - break; - } - // no cache, need full auth with rsa key (openssl required) -#ifdef SW_MYSQL_RSA_SUPPORT - // tell the server we are prepared - do { - mysql::auth_signature_prepared_packet prepared(request.header.number + 1); - if (sw_unlikely(!send_packet(&prepared))) { - return false; - } - } while (0); - // recv rsa key and encode the password - do { - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::raw_data_packet raw_data_packet(data); - mysql::auth_signature_response_packet response( - &raw_data_packet, password, greeting_packet.auth_plugin_data); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - } while (0); - break; -#else - error_code = EPROTONOSUPPORT; - error_msg = SW_MYSQL_NO_RSA_ERROR; - return false; -#endif - } - case SW_MYSQL_PACKET_OK: { -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; - } - case SW_MYSQL_PACKET_ERR: - server_error(data); - return false; - case SW_MYSQL_PACKET_NULL: - // io_error - return false; - default: - _proto_error: - proto_error(data, SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST); - return false; - } - // maybe ok packet or err packet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; -} - -void mysql_client::query(zval *return_value, const char *statement, size_t statement_length) { - send_query_request(return_value, statement, statement_length); - if (EXPECTED(!defer && Z_TYPE_P(return_value) == IS_TRUE)) { - recv_query_response(return_value); - } -} - -void mysql_client::send_query_request(zval *return_value, const char *statement, size_t statement_length) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - if (sw_unlikely(!send_command(SW_MYSQL_COM_QUERY, statement, statement_length))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY; - RETURN_TRUE; -}; - -void mysql_client::recv_query_response(zval *return_value) { - const char *data; - if (sw_unlikely(!(data = recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - state = ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - result.alloc_fields(lcb_packet.length); - for (uint32_t i = 0; i < lcb_packet.length; i++) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = recv_eof_packet()))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY_FETCH; - if (get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -const char *mysql_client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) { - const char *p, *data; - SW_ASSERT(size < sizeof(row_data->stack_buffer)); - if (sw_unlikely(!(p = row_data->read(size)))) { - uint8_t received = row_data->recv(row_data->stack_buffer, size); - if (sw_unlikely(!(data = recv_packet()))) { - return nullptr; - } - row_data->next_packet(data); - received += row_data->recv(row_data->stack_buffer + received, size - received); - if (sw_unlikely(received != size)) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return nullptr; - } - p = row_data->stack_buffer; - } - return p; -} - -bool mysql_client::handle_row_data_lcb(mysql::row_data *row_data) { - const char *p, *data; - // recv 1 byte to get binary code size - if (sw_unlikely(row_data->eof())) { - if (sw_unlikely(!(data = recv_packet()))) { - return false; - } - row_data->next_packet(data); - if (sw_unlikely(row_data->eof())) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return false; - } - } - // decode lcb (use 0 to prevent read_ptr from moving) - // recv "size" bytes to get binary code length - p = handle_row_data_size(row_data, mysql::read_lcb_size(row_data->read(0))); - if (sw_unlikely(!p)) { - return false; - } - mysql::read_lcb(p, &row_data->text.length, &row_data->text.nul); - return true; -} - -void mysql_client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) { - const char *p, *data; - if (sw_unlikely(!handle_row_data_lcb(row_data))) { - RETURN_FALSE; - } - if (sw_unlikely(!(p = row_data->read(row_data->text.length)))) { - size_t received = 0, required = row_data->text.length; - if (required < sizeof(row_data->stack_buffer)) { - p = handle_row_data_size(row_data, required); - if (sw_unlikely(!p)) { - RETURN_FALSE; - } - } else { - zend_string *zstring = zend_string_alloc(required, 0); - do { - received += row_data->recv(ZSTR_VAL(zstring) + received, required - received); - if (received == required) { - break; - } - if (row_data->eof()) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - row_data->next_packet(data); - } - } while (true); - ZSTR_VAL(zstring)[ZSTR_LEN(zstring)] = '\0'; - RETVAL_STR(zstring); - goto _return; - } - } - if (row_data->text.nul || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - RETURN_NULL(); - } else { - RETVAL_STRINGL(p, row_data->text.length); - _return: - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "%.*s=[%lu]%.*s%s", - field->name_length, - field->name, - Z_STRLEN_P(return_value), - (int) SW_MIN(32, Z_STRLEN_P(return_value)), - Z_STRVAL_P(return_value), - (Z_STRLEN_P(return_value) > 32 ? "..." : "")); - } -} - -void mysql_client::handle_strict_type(zval *ztext, mysql::field_packet *field) { - if (sw_likely(Z_TYPE_P(ztext) == IS_STRING)) { - char *error; - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: - /* Date Time */ - case SW_MYSQL_TYPE_TIME: - case SW_MYSQL_TYPE_YEAR: - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: - case SW_MYSQL_TYPE_DATE: - case SW_MYSQL_TYPE_JSON: - return; - /* Integer */ - case SW_MYSQL_TYPE_TINY: - case SW_MYSQL_TYPE_SHORT: - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - ulong_t uint = strtoul(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, uint); - } - } else { - long sint = strtol(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sint); - } - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - unsigned long long ubigint = strtoull(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0' && ubigint <= ZEND_LONG_MAX)) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, ubigint); - } - } else { - long long sbigint = strtoll(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sbigint); - } - } - break; - case SW_MYSQL_TYPE_FLOAT: - case SW_MYSQL_TYPE_DOUBLE: { - double mdouble = strtod(Z_STRVAL_P(ztext), &error); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_DOUBLE(ztext, mdouble); - } - break; - } - default: { - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - break; - } - } - } -} - -void mysql_client::fetch(zval *return_value) { - if (sw_unlikely(!is_connected())) { - RETURN_FALSE; - } - if (sw_unlikely(state != SW_MYSQL_STATE_QUERY_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - zval ztext; - handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - if (strict_type) { - handle_strict_type(&ztext, field); - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - } - } while (0); -} - -void mysql_client::fetch_all(zval *return_value) { - array_init(return_value); - while (true) { - zval zrow; - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void mysql_client::next_result(zval *return_value) { - if (sw_unlikely(state == SW_MYSQL_STATE_QUERY_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(state == SW_MYSQL_STATE_QUERY_MORE_RESULTS)) { - recv_query_response(return_value); - } else if (state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -bool mysql_client::send_prepare_request(const char *statement, size_t statement_length) { - this->statement = new mysql_statement(this, statement, statement_length); - if (sw_unlikely(!this->statement->send_prepare_request())) { - delete this->statement; - this->statement = nullptr; - return false; - } - return true; -} - -mysql_statement *mysql_client::recv_prepare_response() { - if (sw_likely(state == SW_MYSQL_STATE_PREPARE)) { - mysql_statement *statement = this->statement; - SW_ASSERT(statement != nullptr); - this->statement = nullptr; - if (sw_unlikely(!statement->recv_prepare_response())) { - delete statement; - return nullptr; - } - statements[statement->info.id] = statement; - return statement; - } - return nullptr; -} - -void mysql_client::close() { - state = SW_MYSQL_STATE_CLOSED; - Socket *socket = this->socket; - if (socket) { - del_timeout_controller(); - if (!quit && is_writable()) { - send_command_without_check(SW_MYSQL_COM_QUIT); - quit = true; - } - // make statements non-available - while (!statements.empty()) { - auto i = statements.begin(); - i->second->close(false); - statements.erase(i); - } - if (sw_likely(!socket->has_bound())) { - this->socket = nullptr; - } - if (sw_likely(socket->close())) { - delete socket; - } - } -} - -bool mysql_statement::send_prepare_request() { - if (sw_unlikely(!is_available_for_new_request())) { - return false; - } - if (sw_unlikely(!client->send_command(SW_MYSQL_COM_STMT_PREPARE, statement.c_str(), statement.length()))) { - return false; - } - client->state = SW_MYSQL_STATE_PREPARE; - return true; -} - -bool mysql_statement::recv_prepare_response() { - if (sw_unlikely(!is_available())) { - return false; - } else { - client->state = SW_MYSQL_STATE_IDLE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - return false; - } - info = mysql::statement(data); - if (sw_likely(info.param_count != 0)) { - for (uint16_t i = info.param_count; i--;) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::param_packet param_packet(data); -#endif - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - if (info.field_count != 0) { - result.alloc_fields(info.field_count); - for (uint16_t i = 0; i < info.field_count; i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } - result.set_field(i, data); - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - return true; -} - -void mysql_statement::execute(zval *return_value, zval *params) { - send_execute_request(return_value, params); - /* Notice: must check return_value first */ - if (EXPECTED(Z_TYPE_P(return_value) == IS_TRUE && !client->get_defer())) { - recv_execute_response(return_value); - } -} - -void mysql_statement::send_execute_request(zval *return_value, zval *params) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - - uint32_t param_count = params ? php_swoole_array_length(params) : 0; - - if (sw_unlikely(param_count != info.param_count)) { - client->non_sql_error(MYSQLND_CR_INVALID_PARAMETER_NO, - "Statement#%u expects %u parameter, %u given.", - info.id, - info.param_count, - param_count); - RETURN_FALSE; - } - - String *buffer = client->socket->get_write_buffer(); - char *p = buffer->str; - - memset(p, 0, 5); - // command - buffer->str[4] = SW_MYSQL_COM_STMT_EXECUTE; - buffer->length = 5; - p += 5; - - // stmt.id - sw_mysql_int4store(p, info.id); - p += 4; - // flags = CURSOR_TYPE_NO_CURSOR - sw_mysql_int1store(p, 0); - p += 1; - // iteration_count - sw_mysql_int4store(p, 1); - p += 4; - buffer->length += 9; - - // TODO: support more types - if (param_count != 0) { - // null bitmap - size_t null_start_offset = p - buffer->str; - unsigned int map_size = (param_count + 7) / 8; - memset(p, 0, map_size); - p += map_size; - buffer->length += map_size; - - // rebind - sw_mysql_int1store(p, 1); - p += 1; - buffer->length += 1; - - size_t type_start_offset = p - buffer->str; - p += param_count * 2; - buffer->length += param_count * 2; - - char stack_buffer[10]; - zend_ulong index = 0; - zval *value; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), value) { - switch (client->strict_type ? Z_TYPE_P(value) : (IS_NULL == Z_TYPE_P(value) ? IS_NULL : IS_STRING)) { - case IS_NULL: - *((buffer->str + null_start_offset) + (index / 8)) |= (1UL << (index % 8)); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_NULL); - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_LONGLONG); - sw_mysql_int8store(stack_buffer, zval_get_long(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_LONGLONG)) < 0) { - RETURN_FALSE; - } - break; - case IS_DOUBLE: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_DOUBLE); - sw_mysql_doublestore(stack_buffer, zval_get_double(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_DOUBLE)) < 0) { - RETURN_FALSE; - } - break; - default: - zend::String str_value(value); - uint8_t lcb_size = mysql::write_lcb(stack_buffer, str_value.len()); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_VAR_STRING); - if (buffer->append(stack_buffer, lcb_size) < 0) { - RETURN_FALSE; - } - if (buffer->append(str_value.val(), str_value.len()) < 0) { - RETURN_FALSE; - } - } - index++; - } - ZEND_HASH_FOREACH_END(); - } - do { - size_t length = buffer->length - SW_MYSQL_PACKET_HEADER_SIZE; - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, 0); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE + send_s))) { - RETURN_FALSE; - } - if (sw_unlikely(length > SW_MYSQL_MAX_PACKET_BODY_SIZE)) { - size_t send_n = SW_MYSQL_MAX_PACKET_BODY_SIZE, number = 1; - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, number++); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE)) || - !client->send_raw(buffer->str + SW_MYSQL_PACKET_HEADER_SIZE + send_n, send_s)) { - RETURN_FALSE; - } - send_n += send_s; - } - } - } while (0); - client->state = SW_MYSQL_STATE_EXECUTE; - RETURN_TRUE; -} - -void mysql_statement::recv_execute_response(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - client->state = - ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - client->proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - // although we have already known the field data when we prepared the statement, - // we don't know if the data is always reliable, such as when we using stored procedure... - // so we should not optimize here for the time being for stability - result.alloc_fields(lcb_packet.length); - for (size_t i = 0; i < result.get_fields_length(); i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - RETURN_FALSE; - } - client->state = SW_MYSQL_STATE_EXECUTE_FETCH; - if (client->get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -void mysql_statement::fetch(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state != SW_MYSQL_STATE_EXECUTE_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - client->state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - uint32_t null_bitmap_size = mysql::null_bitmap::get_size(result.get_fields_length()); - mysql::null_bitmap null_bitmap(row_data.read(null_bitmap_size), null_bitmap_size); - - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - - /* to check Null-Bitmap @see https://dev.mysql.com/doc/internals/en/null-bitmap.html */ - if (null_bitmap.is_null(i) || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - add_assoc_null_ex(return_value, field->name, field->name_length); - continue; - } - - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_JSON: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: { - _add_string: - zval ztext; - client->handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - break; - } - default: { - const char *p = nullptr; - uint8_t lcb = mysql::get_static_type_size(field->type); - if (lcb == 0) { - client->handle_row_data_lcb(&row_data); - lcb = row_data.text.length; - } - p = client->handle_row_data_size(&row_data, lcb); - if (sw_unlikely(!p)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - /* Date Time */ - switch (field->type) { - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: { - std::string datetime = mysql::datetime(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) datetime.c_str(), datetime.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str()); - break; - } - case SW_MYSQL_TYPE_TIME: { - std::string time = mysql::time(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) time.c_str(), time.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, time.c_str()); - break; - } - case SW_MYSQL_TYPE_DATE: { - std::string date = mysql::date(p, row_data.text.length); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) date.c_str(), date.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, date.c_str()); - break; - } - case SW_MYSQL_TYPE_YEAR: { - add_assoc_long_ex(return_value, field->name, field->name_length, sw_mysql_uint2korr2korr(p)); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, sw_mysql_uint2korr2korr(p)); - break; - } - /* Number */ - case SW_MYSQL_TYPE_TINY: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint8_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int8_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p); - } - break; - case SW_MYSQL_TYPE_SHORT: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint16_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int16_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p); - } - break; - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint32_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int32_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p); - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%lu", field->name_length, field->name, *(uint64_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%ld", field->name_length, field->name, *(int64_t *) p); - } - break; - case SW_MYSQL_TYPE_FLOAT: { - double dv = sw_php_math_round(*(float *) p, 5, PHP_ROUND_HALF_DOWN); - add_assoc_double_ex(return_value, field->name, field->name_length, dv); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.7f", field->name_length, field->name, dv); - } break; - case SW_MYSQL_TYPE_DOUBLE: { - add_assoc_double_ex(return_value, field->name, field->name_length, *(double *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); - } break; - default: - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - goto _add_string; - } - } - } - } - } while (0); -} - -void mysql_statement::fetch_all(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - - zval zrow; - array_init(return_value); - while (true) { - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void mysql_statement::next_result(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state == SW_MYSQL_STATE_EXECUTE_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(client->state == SW_MYSQL_STATE_EXECUTE_MORE_RESULTS)) { - recv_execute_response(return_value); - } else if (client->state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -static sw_inline mysql_coro_t *php_swoole_mysql_coro_fetch_object(zend_object *obj) { - return (mysql_coro_t *) ((char *) obj - swoole_mysql_coro_handlers.offset); -} - -static sw_inline mysql_client *php_swoole_get_mysql_client(zval *zobject) { - return php_swoole_mysql_coro_fetch_object(Z_OBJ_P(zobject))->client; -} - -static void php_swoole_mysql_coro_free_object(zend_object *object) { - mysql_coro_t *zmc = php_swoole_mysql_coro_fetch_object(object); - delete zmc->client; - zend_object_std_dtor(&zmc->std); -} - -static zend_object *php_swoole_mysql_coro_create_object(zend_class_entry *ce) { - mysql_coro_t *zmc = (mysql_coro_t *) zend_object_alloc(sizeof(mysql_coro_t), ce); - zend_object_std_init(&zmc->std, ce); - object_properties_init(&zmc->std, ce); - zmc->std.handlers = &swoole_mysql_coro_handlers; - zmc->client = new mysql_client; - return &zmc->std; -} - -static sw_inline mysql_coro_statement_t *php_swoole_mysql_coro_statement_fetch_object(zend_object *obj) { - return (mysql_coro_statement_t *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset); -} - -static sw_inline mysql_statement *php_swoole_get_mysql_statement(zval *zobject) { - return php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement; -} - -static void php_swoole_mysql_coro_statement_free_object(zend_object *object) { - mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(object); - delete zms->statement; - OBJ_RELEASE(zms->zclient); - zend_object_std_dtor(&zms->std); -} - -static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce, - mysql_statement *statement, - zend_object *client) { - zval zobject; - mysql_coro_statement_t *zms = (mysql_coro_statement_t *) zend_object_alloc(sizeof(mysql_coro_statement_t), ce); - zend_object_std_init(&zms->std, ce); - object_properties_init(&zms->std, ce); - zms->std.handlers = &swoole_mysql_coro_statement_handlers; - ZVAL_OBJ(&zobject, &zms->std); - zend_update_property_long(ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("id"), statement->info.id); - zms->statement = statement; - zms->zclient = client; - GC_ADDREF(client); - return &zms->std; -} - -static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(mysql_statement *statement, - zend_object *client) { - return php_swoole_mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client); -} - -static zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce) { - php_swoole_fatal_error(E_ERROR, "you must create mysql statement object by prepare method"); - return nullptr; -} - -static sw_inline void swoole_mysql_coro_sync_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - SW_ASSERT(instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_ce) || - instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_statement_ce)); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errno"), error_code); - zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), error_msg); - if (!connected) { - zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), connected); - } -} - -static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject, - mysql_client *mc, - zval *return_value) { - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &mc->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - swoole_mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg()); - break; - } - default: - break; - } -} - -static sw_inline void swoole_mysql_coro_sync_execute_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - swoole_mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected); - - /* backward compatibility (sync error info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient); - swoole_mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected); -} - -static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zobject, zval *return_value) { - mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject)); - mysql_statement *ms = zms->statement; - - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &ms->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - - /* backward compatibility (sync result info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, zms->zclient); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - swoole_mysql_coro_sync_execute_error_properties(zobject, ms->get_error_code(), ms->get_error_msg()); - break; - } - default: - break; - } -} - -void php_swoole_mysql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", nullptr, "Co\\MySQL", swoole_mysql_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_mysql_coro, php_swoole_mysql_coro_create_object, php_swoole_mysql_coro_free_object, mysql_coro_t, std); - - SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, - "Swoole\\Coroutine\\MySQL\\Statement", - nullptr, - "Co\\MySQL\\Statement", - swoole_mysql_coro_statement_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_statement); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_statement, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_statement, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_mysql_coro_statement, - php_swoole_mysql_coro_statement_create_object, - php_swoole_mysql_coro_statement_free_object, - mysql_coro_statement_t, - std); - - SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception, - "Swoole\\Coroutine\\MySQL\\Exception", - nullptr, - "Co\\MySQL\\Exception", - nullptr, - swoole_exception); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_exception); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_exception, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_exception, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_mysql_coro_exception); - - zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_mysql_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("connect_errno"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("connect_error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_statement_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_UNKNOWN_ERROR", MYSQLND_CR_UNKNOWN_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CONNECTION_ERROR", MYSQLND_CR_CONNECTION_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR", MYSQLND_CR_SERVER_GONE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_OUT_OF_MEMORY", MYSQLND_CR_OUT_OF_MEMORY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_LOST", MYSQLND_CR_SERVER_LOST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_COMMANDS_OUT_OF_SYNC", MYSQLND_CR_COMMANDS_OUT_OF_SYNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CANT_FIND_CHARSET", MYSQLND_CR_CANT_FIND_CHARSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_MALFORMED_PACKET", MYSQLND_CR_MALFORMED_PACKET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NOT_IMPLEMENTED", MYSQLND_CR_NOT_IMPLEMENTED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NO_PREPARE_STMT", MYSQLND_CR_NO_PREPARE_STMT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_PARAMS_NOT_BOUND", MYSQLND_CR_PARAMS_NOT_BOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO", MYSQLND_CR_INVALID_PARAMETER_NO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_BUFFER_USE", MYSQLND_CR_INVALID_BUFFER_USE); -} - -static PHP_METHOD(swoole_mysql_coro, __construct) {} -static PHP_METHOD(swoole_mysql_coro, __destruct) {} - -static PHP_METHOD(swoole_mysql_coro, connect) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - zval *zserver_info = nullptr; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(zserver_info, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zserver_info) { - HashTable *ht = Z_ARRVAL_P(zserver_info); - zval *ztmp; - - if (php_swoole_array_get_value(ht, "host", ztmp)) { - mc->host = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [host] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "port", ztmp)) { - mc->port = zval_get_long(ztmp); - } - if (php_swoole_array_get_value(ht, "ssl", ztmp)) { - mc->ssl = zval_is_true(ztmp); -#ifndef SW_USE_OPENSSL - if (sw_unlikely(mc->ssl)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPROTONOSUPPORT, - "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - RETURN_FALSE; - } -#endif - } - if (php_swoole_array_get_value(ht, "user", ztmp)) { - mc->user = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [user] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "password", ztmp)) { - mc->password = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [password] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "database", ztmp)) { - mc->database = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [database] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "timeout", ztmp)) { - mc->connect_timeout = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(ht, "charset", ztmp)) { - zend::String zstr_charset(ztmp); - char charset = mysql::get_charset(zstr_charset.val()); - if (UNEXPECTED(charset < 0)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, EINVAL, "Unknown charset [%s]", zstr_charset.val()); - RETURN_FALSE; - } - mc->charset = charset; - } - if (php_swoole_array_get_value(ht, "strict_type", ztmp)) { - mc->strict_type = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(ht, "fetch_mode", ztmp)) { - if (UNEXPECTED(!mc->set_fetch_mode(zval_is_true(ztmp)))) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - RETURN_FALSE; - } - } - } - if (!mc->connect()) { - zend_update_property_long( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_errno"), mc->get_error_code()); - zend_update_property_string( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_error"), mc->get_error_msg()); - RETURN_FALSE; - } - if (zserver_info && php_swoole_array_length(zserver_info) > 0) { - php_array_merge(Z_ARRVAL_P(sw_zend_read_and_convert_property_array( - swoole_mysql_coro_ce, ZEND_THIS, ZEND_STRL("serverInfo"), 0)), - Z_ARRVAL_P(zserver_info)); - } - zend_update_property_long(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("sock"), mc->get_fd()); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro, getDefer) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - RETURN_BOOL(mc->get_defer()); -} - -static PHP_METHOD(swoole_mysql_coro, setDefer) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - zend_bool defer = 1; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(defer) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - bool ret = mc->set_defer(defer); - if (UNEXPECTED(!ret)) { - zend_throw_exception_ex(swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - } - RETURN_BOOL(ret); -} - -static PHP_METHOD(swoole_mysql_coro, query) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - char *sql; - size_t sql_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(sql, sql_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, sql, sql_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, fetch) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, fetchAll) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch_all(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, nextResult) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->next_result(return_value); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro, prepare) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - char *statement; - size_t statement_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(statement, statement_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) { - _failed: - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - RETVAL_FALSE; - } else if (UNEXPECTED(mc->get_defer())) { - RETVAL_TRUE; - } else { - mysql_statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - goto _failed; - } - RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - mc->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro, recv) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!mc->check_connection())) { - swoole_mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); - RETURN_FALSE; - } - mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch (mc->state) { - case SW_MYSQL_STATE_IDLE: - swoole_mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_QUERY: - mc->recv_query_response(return_value); - break; - case SW_MYSQL_STATE_PREPARE: { - mysql_statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - RETVAL_FALSE; - } else { - RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - break; - } - default: - if (UNEXPECTED(mc->state & SW_MYSQL_COMMAND_FLAG_EXECUTE)) { - swoole_mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data"); - } else { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - mc->del_timeout_controller(); -} - -static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS, - const char *command, - size_t command_length) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(mc->get_defer())) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPERM, - "you should not query transaction when defer mode is on, if you want, please use `query('%s')` instead", - command); - RETURN_FALSE; - } - - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, command, command_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, begin) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BEGIN")); -} - -static PHP_METHOD(swoole_mysql_coro, commit) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("COMMIT")); -} - -static PHP_METHOD(swoole_mysql_coro, rollback) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLLBACK")); -} - -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - char *str; - size_t str_length; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(str, str_length) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - char *newstr = (char *) safe_emalloc(2, str_length + 1, 1); - const MYSQLND_CHARSET *cset = mysqlnd_find_charset_nr(mc->charset); - if (!cset) { - php_swoole_fatal_error(E_ERROR, "unknown mysql charset[%d]", mc->charset); - RETURN_FALSE; - } - zend_ulong newstr_len = mysqlnd_cset_escape_slashes(cset, newstr, str, str_length); - if (newstr_len == (zend_ulong) ~0) { - php_swoole_fatal_error(E_ERROR, "mysqlnd_cset_escape_slashes() failed"); - RETURN_FALSE; - } - RETVAL_STRINGL(newstr, newstr_len); - efree(newstr); - return; -} -#endif - -static PHP_METHOD(swoole_mysql_coro, close) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); - mc->close(); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro_statement, execute) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - zval *params = nullptr; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(params, 1, 0) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->execute(return_value, params); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetch) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch_all(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->next_result(return_value); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - mysql_client *mc = ms->get_client(); - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, recv) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - double timeout = 0; - enum sw_mysql_state state; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - ms->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch ((state = ms->get_client()->state)) { - case SW_MYSQL_STATE_IDLE: - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_EXECUTE: - ms->recv_execute_response(return_value); - break; - default: - if (UNEXPECTED(state & SW_MYSQL_COMMAND_FLAG_QUERY)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, EPERM, "please use client to receive data"); - } else { - swoole_mysql_coro_sync_execute_error_properties( - ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - ms->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro_statement, close) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); - ms->close(); - RETURN_TRUE; -} diff --git a/ext-src/swoole_mysql_proto.cc b/ext-src/swoole_mysql_proto.cc deleted file mode 100644 index fac8cd809bb..00000000000 --- a/ext-src/swoole_mysql_proto.cc +++ /dev/null @@ -1,744 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2015 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http:// www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_mysql_proto.h" - -using namespace swoole::mysql; - -namespace swoole { -namespace mysql { -struct charset_t { - uint nr; - const char *name; - const char *collation; -}; - -char get_charset(const char *name) { - static const charset_t charsets[] = { - {1, "big5", "big5_chinese_ci"}, - {3, "dec8", "dec8_swedish_ci"}, - {4, "cp850", "cp850_general_ci"}, - {6, "hp8", "hp8_english_ci"}, - {7, "koi8r", "koi8r_general_ci"}, - {8, "latin1", "latin1_swedish_ci"}, - {5, "latin1", "latin1_german1_ci"}, - {9, "latin2", "latin2_general_ci"}, - {2, "latin2", "latin2_czech_cs"}, - {10, "swe7", "swe7_swedish_ci"}, - {11, "ascii", "ascii_general_ci"}, - {12, "ujis", "ujis_japanese_ci"}, - {13, "sjis", "sjis_japanese_ci"}, - {16, "hebrew", "hebrew_general_ci"}, - {17, "filename", "filename"}, - {18, "tis620", "tis620_thai_ci"}, - {19, "euckr", "euckr_korean_ci"}, - {21, "latin2", "latin2_hungarian_ci"}, - {27, "latin2", "latin2_croatian_ci"}, - {22, "koi8u", "koi8u_general_ci"}, - {24, "gb2312", "gb2312_chinese_ci"}, - {25, "greek", "greek_general_ci"}, - {26, "cp1250", "cp1250_general_ci"}, - {28, "gbk", "gbk_chinese_ci"}, - {30, "latin5", "latin5_turkish_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {15, "latin1", "latin1_danish_ci"}, - {32, "armscii8", "armscii8_general_ci"}, - {33, "utf8", "utf8_general_ci"}, - {35, "ucs2", "ucs2_general_ci"}, - {36, "cp866", "cp866_general_ci"}, - {37, "keybcs2", "keybcs2_general_ci"}, - {38, "macce", "macce_general_ci"}, - {39, "macroman", "macroman_general_ci"}, - {40, "cp852", "cp852_general_ci"}, - {41, "latin7", "latin7_general_ci"}, - {20, "latin7", "latin7_estonian_cs"}, - {57, "cp1256", "cp1256_general_ci"}, - {59, "cp1257", "cp1257_general_ci"}, - {63, "binary", "binary"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {29, "cp1257", "cp1257_lithuanian_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {34, "cp1250", "cp1250_czech_cs"}, - {42, "latin7", "latin7_general_cs"}, - {43, "macce", "macce_bin"}, - {44, "cp1250", "cp1250_croatian_ci"}, - {45, "utf8mb4", "utf8mb4_general_ci"}, - {46, "utf8mb4", "utf8mb4_bin"}, - {47, "latin1", "latin1_bin"}, - {48, "latin1", "latin1_general_ci"}, - {49, "latin1", "latin1_general_cs"}, - {51, "cp1251", "cp1251_general_ci"}, - {14, "cp1251", "cp1251_bulgarian_ci"}, - {23, "cp1251", "cp1251_ukrainian_ci"}, - {50, "cp1251", "cp1251_bin"}, - {52, "cp1251", "cp1251_general_cs"}, - {53, "macroman", "macroman_bin"}, - {54, "utf16", "utf16_general_ci"}, - {55, "utf16", "utf16_bin"}, - {56, "utf16le", "utf16le_general_ci"}, - {58, "cp1257", "cp1257_bin"}, - {60, "utf32", "utf32_general_ci"}, - {61, "utf32", "utf32_bin"}, - {62, "utf16le", "utf16le_bin"}, - {64, "armscii8", "armscii8_bin"}, - {65, "ascii", "ascii_bin"}, - {66, "cp1250", "cp1250_bin"}, - {67, "cp1256", "cp1256_bin"}, - {68, "cp866", "cp866_bin"}, - {69, "dec8", "dec8_bin"}, - {70, "greek", "greek_bin"}, - {71, "hebrew", "hebrew_bin"}, - {72, "hp8", "hp8_bin"}, - {73, "keybcs2", "keybcs2_bin"}, - {74, "koi8r", "koi8r_bin"}, - {75, "koi8u", "koi8u_bin"}, - {77, "latin2", "latin2_bin"}, - {78, "latin5", "latin5_bin"}, - {79, "latin7", "latin7_bin"}, - {80, "cp850", "cp850_bin"}, - {81, "cp852", "cp852_bin"}, - {82, "swe7", "swe7_bin"}, - {83, "utf8", "utf8_bin"}, - {84, "big5", "big5_bin"}, - {85, "euckr", "euckr_bin"}, - {86, "gb2312", "gb2312_bin"}, - {87, "gbk", "gbk_bin"}, - {88, "sjis", "sjis_bin"}, - {89, "tis620", "tis620_bin"}, - {90, "ucs2", "ucs2_bin"}, - {91, "ujis", "ujis_bin"}, - {92, "geostd8", "geostd8_general_ci"}, - {93, "geostd8", "geostd8_bin"}, - {94, "latin1", "latin1_spanish_ci"}, - {95, "cp932", "cp932_japanese_ci"}, - {96, "cp932", "cp932_bin"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {98, "eucjpms", "eucjpms_bin"}, - {99, "cp1250", "cp1250_polish_ci"}, - {128, "ucs2", "ucs2_unicode_ci"}, - {129, "ucs2", "ucs2_icelandic_ci"}, - {130, "ucs2", "ucs2_latvian_ci"}, - {131, "ucs2", "ucs2_romanian_ci"}, - {132, "ucs2", "ucs2_slovenian_ci"}, - {133, "ucs2", "ucs2_polish_ci"}, - {134, "ucs2", "ucs2_estonian_ci"}, - {135, "ucs2", "ucs2_spanish_ci"}, - {136, "ucs2", "ucs2_swedish_ci"}, - {137, "ucs2", "ucs2_turkish_ci"}, - {138, "ucs2", "ucs2_czech_ci"}, - {139, "ucs2", "ucs2_danish_ci"}, - {140, "ucs2", "ucs2_lithuanian_ci"}, - {141, "ucs2", "ucs2_slovak_ci"}, - {142, "ucs2", "ucs2_spanish2_ci"}, - {143, "ucs2", "ucs2_roman_ci"}, - {144, "ucs2", "ucs2_persian_ci"}, - {145, "ucs2", "ucs2_esperanto_ci"}, - {146, "ucs2", "ucs2_hungarian_ci"}, - {147, "ucs2", "ucs2_sinhala_ci"}, - {148, "ucs2", "ucs2_german2_ci"}, - {149, "ucs2", "ucs2_croatian_ci"}, - {150, "ucs2", "ucs2_unicode_520_ci"}, - {151, "ucs2", "ucs2_vietnamese_ci"}, - {160, "utf32", "utf32_unicode_ci"}, - {161, "utf32", "utf32_icelandic_ci"}, - {162, "utf32", "utf32_latvian_ci"}, - {163, "utf32", "utf32_romanian_ci"}, - {164, "utf32", "utf32_slovenian_ci"}, - {165, "utf32", "utf32_polish_ci"}, - {166, "utf32", "utf32_estonian_ci"}, - {167, "utf32", "utf32_spanish_ci"}, - {168, "utf32", "utf32_swedish_ci"}, - {169, "utf32", "utf32_turkish_ci"}, - {170, "utf32", "utf32_czech_ci"}, - {171, "utf32", "utf32_danish_ci"}, - {172, "utf32", "utf32_lithuanian_ci"}, - {173, "utf32", "utf32_slovak_ci"}, - {174, "utf32", "utf32_spanish2_ci"}, - {175, "utf32", "utf32_roman_ci"}, - {176, "utf32", "utf32_persian_ci"}, - {177, "utf32", "utf32_esperanto_ci"}, - {178, "utf32", "utf32_hungarian_ci"}, - {179, "utf32", "utf32_sinhala_ci"}, - {180, "utf32", "utf32_german2_ci"}, - {181, "utf32", "utf32_croatian_ci"}, - {182, "utf32", "utf32_unicode_520_ci"}, - {183, "utf32", "utf32_vietnamese_ci"}, - {192, "utf8", "utf8_unicode_ci"}, - {193, "utf8", "utf8_icelandic_ci"}, - {194, "utf8", "utf8_latvian_ci"}, - {195, "utf8", "utf8_romanian_ci"}, - {196, "utf8", "utf8_slovenian_ci"}, - {197, "utf8", "utf8_polish_ci"}, - {198, "utf8", "utf8_estonian_ci"}, - {199, "utf8", "utf8_spanish_ci"}, - {200, "utf8", "utf8_swedish_ci"}, - {201, "utf8", "utf8_turkish_ci"}, - {202, "utf8", "utf8_czech_ci"}, - {203, "utf8", "utf8_danish_ci"}, - {204, "utf8", "utf8_lithuanian_ci"}, - {205, "utf8", "utf8_slovak_ci"}, - {206, "utf8", "utf8_spanish2_ci"}, - {207, "utf8", "utf8_roman_ci"}, - {208, "utf8", "utf8_persian_ci"}, - {209, "utf8", "utf8_esperanto_ci"}, - {210, "utf8", "utf8_hungarian_ci"}, - {211, "utf8", "utf8_sinhala_ci"}, - {212, "utf8", "utf8_german2_ci"}, - {213, "utf8", "utf8_croatian_ci"}, - {214, "utf8", "utf8_unicode_520_ci"}, - {215, "utf8", "utf8_vietnamese_ci"}, - {224, "utf8mb4", "utf8mb4_unicode_ci"}, - {225, "utf8mb4", "utf8mb4_icelandic_ci"}, - {226, "utf8mb4", "utf8mb4_latvian_ci"}, - {227, "utf8mb4", "utf8mb4_romanian_ci"}, - {228, "utf8mb4", "utf8mb4_slovenian_ci"}, - {229, "utf8mb4", "utf8mb4_polish_ci"}, - {230, "utf8mb4", "utf8mb4_estonian_ci"}, - {231, "utf8mb4", "utf8mb4_spanish_ci"}, - {232, "utf8mb4", "utf8mb4_swedish_ci"}, - {233, "utf8mb4", "utf8mb4_turkish_ci"}, - {234, "utf8mb4", "utf8mb4_czech_ci"}, - {235, "utf8mb4", "utf8mb4_danish_ci"}, - {236, "utf8mb4", "utf8mb4_lithuanian_ci"}, - {237, "utf8mb4", "utf8mb4_slovak_ci"}, - {238, "utf8mb4", "utf8mb4_spanish2_ci"}, - {239, "utf8mb4", "utf8mb4_roman_ci"}, - {240, "utf8mb4", "utf8mb4_persian_ci"}, - {241, "utf8mb4", "utf8mb4_esperanto_ci"}, - {242, "utf8mb4", "utf8mb4_hungarian_ci"}, - {243, "utf8mb4", "utf8mb4_sinhala_ci"}, - {244, "utf8mb4", "utf8mb4_german2_ci"}, - {245, "utf8mb4", "utf8mb4_croatian_ci"}, - {246, "utf8mb4", "utf8mb4_unicode_520_ci"}, - {247, "utf8mb4", "utf8mb4_vietnamese_ci"}, - {248, "gb18030", "gb18030_chinese_ci"}, - {249, "gb18030", "gb18030_bin"}, - {254, "utf8", "utf8_general_cs"}, - {0, nullptr, nullptr}, - }; - const charset_t *c = charsets; - while (c[0].nr) { - if (!strcasecmp(c->name, name)) { - return c->nr; - } - ++c; - } - return -1; -} - -// clang-format off -uint8_t get_static_type_size(uint8_t type) -{ - static const uint8_t map[] = - { - 0, // SW_MYSQL_TYPE_DECIMAL 0 - sizeof(int8_t), // SW_MYSQL_TYPE_TINY 1 - sizeof(int16_t), // SW_MYSQL_TYPE_SHORT 2 - sizeof(int32_t), // SW_MYSQL_TYPE_LONG 3 - sizeof(float), // SW_MYSQL_TYPE_FLOAT 4 - sizeof(double), // SW_MYSQL_TYPE_DOUBLE 5 - 0, // SW_MYSQL_TYPE_NULL 6 - 0, // SW_MYSQL_TYPE_TIMESTAMP 7 - sizeof(int64_t), // SW_MYSQL_TYPE_LONGLONG 8 - sizeof(int32_t), // SW_MYSQL_TYPE_INT24 9 - 0, // SW_MYSQL_TYPE_DATE 10 - 0, // SW_MYSQL_TYPE_TIME 11 - 0, // SW_MYSQL_TYPE_DATETIME 12 - sizeof(int16_t), // SW_MYSQL_TYPE_YEAR 13 - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 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 - }; - SW_ASSERT(sizeof(map) == UINT8_MAX + 1); - return map[type]; -} -// clang-format on - -static uint32_t sha1_password_with_nonce(char *buf, const char *nonce, const char *password) { - char hash_0[20] = {}; - php_swoole_sha1(password, strlen(password), (uchar *) hash_0); - - char hash_1[20] = {}; - php_swoole_sha1(hash_0, sizeof(hash_0), (uchar *) hash_1); - - char str[40]; - memcpy(str, nonce, 20); - memcpy(str + 20, hash_1, 20); - - char hash_2[20]; - php_swoole_sha1(str, sizeof(str), (uchar *) hash_2); - - char hash_3[20]; - - int *a = (int *) hash_2; - int *b = (int *) hash_0; - int *c = (int *) hash_3; - - int i; - for (i = 0; i < 5; i++) { - c[i] = a[i] ^ b[i]; - } - memcpy(buf, hash_3, 20); - return 20; -} - -static uint32_t sha256_password_with_nonce(char *buf, const char *nonce, const char *password) { - // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) - char hashed[32], double_hashed[32]; - php_swoole_sha256(password, strlen(password), (unsigned char *) hashed); - php_swoole_sha256(hashed, 32, (unsigned char *) double_hashed); - char combined[32 + SW_MYSQL_NONCE_LENGTH]; // double-hashed + nonce - memcpy(combined, double_hashed, 32); - memcpy(combined + 32, nonce, SW_MYSQL_NONCE_LENGTH); - char xor_bytes[32]; - php_swoole_sha256(combined, 32 + SW_MYSQL_NONCE_LENGTH, (unsigned char *) xor_bytes); - int i; - for (i = 0; i < 32; i++) { - hashed[i] ^= xor_bytes[i]; - } - memcpy(buf, hashed, 32); - return 32; -} - -/** @return: password length */ -static sw_inline uint32_t mysql_auth_encrypt_dispatch(char *buf, - const std::string auth_plugin_name, - const char *nonce, - const char *password) { - if (auth_plugin_name.length() == 0 || auth_plugin_name == "mysql_native_password") { - // mysql_native_password is default - return sha1_password_with_nonce(buf, nonce, password); - } else if (auth_plugin_name == "caching_sha2_password") { - return sha256_password_with_nonce(buf, nonce, password); - } else { - swoole_warning("Unknown auth plugin: %s", auth_plugin_name.c_str()); - return 0; - } -} - -eof_packet::eof_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "EOF_Packet"); - // EOF_Packet = Packet header (4 bytes) + 0xFE + warning(2byte) + status(2byte) - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [fe] EOF header - data += 1; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> status_flags Status Flags - server_status = sw_mysql_uint2korr2korr(data); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "EOF_Packet, warnings=%u, status_code=%u", warning_count, server_status.status); -} - -ok_packet::ok_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "OK_Packet"); - bool nul; - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [00] or [fe] the OK packet header - data += 1; - // int affected_rows affected rows - data += read_lcb(data, &affected_rows, &nul); - // int last_insert_id last insert id - data += read_lcb(data, &last_insert_id, &nul); - // int<2> status_flags status Flags - server_status = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - // p += 2; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "OK_Packet, affected_rows=%" PRIu64 ", insert_id=%" PRIu64 ", status_flags=0x%08x, warnings=%u", - affected_rows, - last_insert_id, - server_status.status, - warning_count); -} - -err_packet::err_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "ERR_Packet"); - // ERR Packet = Packet header (4 bytes) + ERR Payload - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [ff] header of the ERR packet - data += 1; - // int<2> error_code error-code - code = sw_mysql_uint2korr2korr(data); - data += 2; - // string[1] sql_state_marker # marker of the SQL State - data += 1; - // string[5] sql_state SQL State - memcpy(sql_state, data, 5); - sql_state[5] = '\0'; - data += 5; - // string error_message human readable error message - msg = std::string(data, header.length - 9); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "ERR_Packet, error_code=%u, sql_state=%s, status_msg=[%s]", - code, - sql_state, - msg.c_str()); -}; - -greeting_packet::greeting_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::HandshakeGreeting"); - /** - 1 [0a] protocol version - string[NUL] server version - 4 connection id - string[8] auth-plugin-data-part-1 - 1 [00] filler - 2 capability flags (lower 2 bytes) - if more data in the packet: - 1 character set - 2 status flags - 2 capability flags (upper 2 bytes) - if capabilities & CLIENT_PLUGIN_AUTH { - 1 length of auth-plugin-data - } else { - 1 [00] - } - string[10] reserved (all [00]) - if capabilities & CLIENT_SECURE_CONNECTION { - string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8)) - if capabilities & CLIENT_PLUGIN_AUTH { - string[NUL] auth-plugin name - } - */ - const char *p = data + SW_MYSQL_PACKET_HEADER_SIZE; - // 1 [0a] protocol version - protocol_version = *p; - p++; - // x server version - server_version = std::string(p); - p += server_version.length() + 1; - // 4 connection id - connection_id = *((int *) p); - p += 4; - // string[8] auth-plugin-data-part-1 - memcpy(auth_plugin_data, p, 8); - p += 8; - // 1 [00] filler - filler = *p; - p += 1; - // 2 capability flags (lower 2 bytes) - memcpy(((char *) (&capability_flags)), p, 2); - p += 2; - - if (p < data + header.length) { - // 1 character set - charset = *p; - p += 1; - // 2 status flags - memcpy(&status_flags, p, 2); - p += 2; - // 2 capability flags (upper 2 bytes) - memcpy(((char *) (&capability_flags) + 2), p, 2); - p += 2; - // 1 auth plugin data length - auth_plugin_data_length = (uint8_t) *p; - p += 1; - // x reserved - memcpy(&reserved, p, sizeof(reserved)); - p += sizeof(reserved); - if (capability_flags & SW_MYSQL_CLIENT_SECURE_CONNECTION) { - uint8_t len = SW_MAX(13, auth_plugin_data_length - 8); - memcpy(auth_plugin_data + 8, p, len); - p += len; - } - if (capability_flags & SW_MYSQL_CLIENT_PLUGIN_AUTH) { - auth_plugin_name = std::string(p, strlen(p)); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", auth_plugin_name.c_str()); - } - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Server protocol=%d, version=%s, connection_id=%d, capabilites=0x%08x, status=%u, auth_plugin_name=%s, " - "auth_plugin_data=L%u[%s]", - protocol_version, - server_version.c_str(), - connection_id, - capability_flags, - status_flags.status, - auth_plugin_name.c_str(), - auth_plugin_data_length, - auth_plugin_data); -}; - -login_packet::login_packet(greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset) { - char *p = data.body; - uint32_t tint; - // capability flags, CLIENT_PROTOCOL_41 always set - tint = SW_MYSQL_CLIENT_LONG_PASSWORD | SW_MYSQL_CLIENT_PROTOCOL_41 | SW_MYSQL_CLIENT_SECURE_CONNECTION | - SW_MYSQL_CLIENT_CONNECT_WITH_DB | SW_MYSQL_CLIENT_PLUGIN_AUTH | SW_MYSQL_CLIENT_MULTI_RESULTS; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client capabilites=0x%08x", tint); - // max-packet size - tint = 300; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client max packet=%u", tint); - // use the server character_set when the character_set is not set. - *p = charset ? charset : greeting_packet->charset; - p += 1; - // string[23] reserved (all [0]) - p += 23; - // string[NUL] username - strcpy(p, user.c_str()); - p += (user.length() + 1); - // string[NUL] password - if (password.length() > 0) { - *p = mysql_auth_encrypt_dispatch( - p + 1, greeting_packet->auth_plugin_name, greeting_packet->auth_plugin_data, password.c_str()); - } else { - *p = 0; - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Client charset=%u, user=%s, password=%s, hased=L%d[%.*s], database=%s, auth_plugin_name=%s", - charset, - user.c_str(), - password.c_str(), - (int) *p, - (int) *p, - p + 1, - database.c_str(), - greeting_packet->auth_plugin_name.c_str()); - p += (((uint32_t) *p) + 1); - // string[NUL] database - strcpy(p, database.c_str()); - p += (database.length() + 1); - // string[NUL] auth plugin name - strcpy(p, greeting_packet->auth_plugin_name.c_str()); - p += (greeting_packet->auth_plugin_name.length() + 1); - // packet header - set_header(p - data.body, greeting_packet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::HandshakeLogin"); -} - -auth_switch_request_packet::auth_switch_request_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSwitchRequest"); - // 4 header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // 1 type - data += 1; - // string[NUL] auth_method_name - auth_method_name = std::string(data); - data += (auth_method_name.length() + 1); - // string[NUL] auth_method_data - strcpy(auth_method_data, data); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_method_name.c_str()); -} - -auth_switch_response_packet::auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password) { - // if auth switch is triggered, password can't be empty - // create auth switch response packet - set_header(mysql_auth_encrypt_dispatch(data.body, req->auth_method_name, req->auth_method_data, password.c_str()), - req->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -} - -// Caching sha2 authentication. Public key request and send encrypted password -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse -auth_signature_response_packet::auth_signature_response_packet(raw_data_packet *raw_data_pakcet, - const std::string &password, - const char *auth_plugin_data) { -#ifndef SW_MYSQL_RSA_SUPPORT - { - swoole_warning(SW_MYSQL_NO_RSA_ERROR); -#else - if (0) { - _error: -#endif - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_ERROR; - set_header(1, raw_data_pakcet->header.number + 1); - return; - } -#ifdef SW_MYSQL_RSA_SUPPORT - const char *tmp = raw_data_pakcet->body; - uint32_t rsa_public_key_length = raw_data_pakcet->header.length; - while (tmp[0] != 0x2d) { - tmp++; // ltrim - rsa_public_key_length--; - } - char rsa_public_key[rsa_public_key_length + 1]; // rsa + '\0' - memcpy((char *) rsa_public_key, tmp, rsa_public_key_length); - rsa_public_key[rsa_public_key_length] = '\0'; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "rsa_public_key_length=%d;\nrsa_public_key=[%.*s]", - rsa_public_key_length, - rsa_public_key_length, - rsa_public_key); - - size_t password_bytes_length = password.length() + 1; - unsigned char password_bytes[password_bytes_length]; - // copy NUL terminator to password to stack - strcpy((char *) password_bytes, password.c_str()); - // XOR the password bytes with the challenge - for (size_t i = 0; i < password_bytes_length; i++) // include '\0' byte - { - password_bytes[i] ^= auth_plugin_data[i % SW_MYSQL_NONCE_LENGTH]; - } - - // prepare RSA public key - BIO *bio = nullptr; - RSA *public_rsa = nullptr; - if (sw_unlikely((bio = BIO_new_mem_buf((void *) rsa_public_key, -1)) == nullptr)) { - swoole_warning("BIO_new_mem_buf publicKey error!"); - goto _error; - } - // PEM_read_bio_RSA_PUBKEY - ERR_clear_error(); - if (sw_unlikely((public_rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr)) == nullptr)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[PEM_read_bio_RSA_PUBKEY ERROR]: %s", err_buf); - goto _error; - } - BIO_free_all(bio); - // encrypt with RSA public key - int rsa_len = RSA_size(public_rsa); - unsigned char encrypt_msg[rsa_len]; - // RSA_public_encrypt - ERR_clear_error(); - size_t flen = rsa_len - 42; - flen = password_bytes_length > flen ? flen : password_bytes_length; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "rsa_len=%d", rsa_len); - if (sw_unlikely(RSA_public_encrypt(flen, - (const unsigned char *) password_bytes, - (unsigned char *) encrypt_msg, - public_rsa, - RSA_PKCS1_OAEP_PADDING) < 0)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[RSA_public_encrypt ERROR]: %s", err_buf); - goto _error; - } - RSA_free(public_rsa); - memcpy(data.body, (char *) encrypt_msg, rsa_len); // copy rsa to buf - set_header(rsa_len, raw_data_pakcet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -#endif -} - -void field_packet::parse(const char *data) { - server_packet::parse(data); - bool nul = false; - char *p = body = new char[header.length]; - memcpy(body, data + SW_MYSQL_PACKET_HEADER_SIZE, header.length); - // catalog - p += read_lcb(p, &catalog_length, &nul); - catalog = p; - p += catalog_length; - // database - p += read_lcb(p, &database_length, &nul); - database = p; - p += database_length; - // table - p += read_lcb(p, &table_length, &nul); - table = p; - p += table_length; - // origin table - p += read_lcb(p, &org_table_length, &nul); - org_table = p; - p += org_table_length; - // name - p += read_lcb(p, &name_length, &nul); - name = p; - p += name_length; - // origin table - p += read_lcb(p, &org_name_length, &nul); - org_name = p; - p += org_name_length; - // filler - p += 1; - // charset - charset = sw_mysql_uint2korr2korr(p); - p += 2; - // binary length - length = sw_mysql_uint2korr4korr(p); - p += 4; - // field type - type = (uint8_t) *p; - p += 1; - // flags - flags = sw_mysql_uint2korr2korr(p); - p += 2; - /* decimals */ - decimals = *p; - p += 1; - /* filler */ - p += 2; - /* default - a priori facultatif */ - if (p < body + header.length) { - p += read_lcb(p, &def_length, &nul); - def = p; - p += def_length; - } - swMysqlPacketDump(header.length, header.number, data, (*name == '?' ? "Protocol::Param" : "Protocol::Field")); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "catalog=%.*s, database=%.*s, table=%.*s, org_table=%.*s, name=%.*s, org_name=%.*s," - "charset=%u, binary_length=%" PRIu64 ", type=%u, flags=0x%08x, decimals=%u, def=[%.*s]", - catalog_length, - catalog, - database_length, - database, - table_length, - table, - org_table_length, - org_table, - name_length, - name, - org_name_length, - org_name, - charset, - length, - type, - flags, - decimals, - def_length, - def); -} -} // namespace mysql -} // namespace swoole diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc new file mode 100644 index 00000000000..8c3b61ec727 --- /dev/null +++ b/ext-src/swoole_name_resolver.cc @@ -0,0 +1,239 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_name_resolver_arginfo.h" +END_EXTERN_C() + +using swoole::NameResolver; + +BEGIN_EXTERN_C() + +#include "ext/spl/php_spl.h" + +zend_class_entry *swoole_name_resolver_context_ce; +zend_object_handlers swoole_name_resolver_context_handlers; + +struct ContextObject { + NameResolver::Context *context; + zend_object std; +}; + +static zend_always_inline NameResolver::Context *swoole_name_resolver_context_get_handle(zend_object *object) { + return ((ContextObject *) ((char *) object - swoole_name_resolver_context_handlers.offset))->context; +} + +static zend_always_inline ContextObject *swoole_name_resolver_context_get_object(zend_object *object) { + return (ContextObject *) ((char *) object - swoole_name_resolver_context_handlers.offset); +} + +static zend_always_inline ContextObject *swoole_name_resolver_context_get_object_safe(zend_object *object) { + NameResolver::Context *name_resolver_context = swoole_name_resolver_context_get_handle(object); + if (UNEXPECTED(!name_resolver_context)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return swoole_name_resolver_context_get_object(object); +} + +static zend_object *swoole_name_resolver_context_create_object(zend_class_entry *ce) { + ContextObject *name_resolver_context_object = + (ContextObject *) zend_object_alloc(sizeof(*name_resolver_context_object), ce); + + zend_object_std_init(&name_resolver_context_object->std, ce); + object_properties_init(&name_resolver_context_object->std, ce); + name_resolver_context_object->std.handlers = &swoole_name_resolver_context_handlers; + name_resolver_context_object->context = new NameResolver::Context(); + + return &name_resolver_context_object->std; +} + +static void swoole_name_resolver_context_free_object(zend_object *object) { + ContextObject *name_resolver_context_object = swoole_name_resolver_context_get_object(object); + delete name_resolver_context_object->context; + zend_object_std_dtor(&name_resolver_context_object->std); +} + +ZEND_METHOD(Swoole_NameResolver_Context, __construct) { + zend_long family = AF_INET; + zend_bool with_port = false; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_BOOL(with_port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(ZEND_THIS)); + obj->context->with_port = with_port; + obj->context->type = family; +} + +void php_swoole_name_resolver_minit(int module_number) { + SW_INIT_CLASS_ENTRY_STD( + swoole_name_resolver_context, "Swoole\\NameResolver\\Context", class_Swoole_NameResolver_Context_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_name_resolver_context); + SW_SET_CLASS_CLONEABLE(swoole_name_resolver_context, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_name_resolver_context, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_name_resolver_context, + swoole_name_resolver_context_create_object, + swoole_name_resolver_context_free_object, + ContextObject, + std); +} + +PHP_FUNCTION(swoole_name_resolver_lookup) { + char *name; + size_t l_name; + zval *zcontext; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, l_name) + Z_PARAM_OBJECT(zcontext) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(zcontext)); + auto result = swoole_name_resolver_lookup(std::string(name, l_name), obj->context); + RETURN_STRINGL(result.c_str(), result.length()); +} + +PHP_FUNCTION(swoole_name_resolver_add) { + zval *zresolver; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(zresolver) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(php_swoole_name_resolver_add(zresolver)); +} + +PHP_FUNCTION(swoole_name_resolver_remove) { + zval *zresolver; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(zresolver) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + auto hash_1 = sw_php_spl_object_hash(zresolver); + bool found = false; + swoole_name_resolver_each( + [&found, hash_1, zresolver](const std::list::iterator &iter) -> swTraverseOperation { + if (found) { + return SW_TRAVERSE_STOP; + } + auto hash_2 = sw_php_spl_object_hash((zval *) iter->private_data); + bool equals = zend_string_equals(hash_2, hash_1); + zend_string_release(hash_2); + if (iter->type == NameResolver::TYPE_PHP && iter->private_data && equals) { + zval_dtor(zresolver); + efree(iter->private_data); + found = true; + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + zend_string_release(hash_1); + RETURN_BOOL(found); +} + +END_EXTERN_C() + +bool php_swoole_name_resolver_add(zval *zresolver) { + auto ce = zend_lookup_class(SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)); + if (ce == nullptr) { + php_swoole_fatal_error( + E_WARNING, "Class \"%s\" not found", SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val); + return false; + } + if (!instanceof_function(Z_OBJCE_P(zresolver), ce)) { + php_swoole_fatal_error(E_WARNING, + "the given object is not an instance of %s", + SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val); + return false; + } + zval_add_ref(zresolver); + NameResolver resolver{php_swoole_name_resolver_lookup, sw_zval_dup(zresolver), NameResolver::TYPE_PHP}; + swoole_name_resolver_add(resolver); + return true; +} + +std::string php_swoole_name_resolver_lookup(const std::string &name, NameResolver::Context *ctx, void *_resolver) { + zval *zcluster_object; + zval retval; + zval *zresolver = (zval *) _resolver; + + if (!ctx->private_data) { + _lookup: + zval zname; + ZVAL_STRINGL(&zname, name.c_str(), name.length()); + zend_call_method_with_1_params(SW_Z8_OBJ_P(zresolver), NULL, NULL, "lookup", &retval, &zname); + zval_dtor(&zname); + if (Z_TYPE(retval) == IS_OBJECT) { + ctx->private_data = zcluster_object = (zval *) ecalloc(1, sizeof(zval)); + ctx->dtor = [](NameResolver::Context *ctx) { + zval *_zcluster_object = (zval *) ctx->private_data; + zval_dtor(_zcluster_object); + efree(_zcluster_object); + }; + *zcluster_object = retval; + ctx->cluster_ = true; + ctx->final_ = false; + } else if (Z_TYPE(retval) == IS_STRING) { + ctx->final_ = true; + ctx->cluster_ = false; + return std::string(Z_STRVAL(retval), Z_STRLEN(retval)); + } else { + ctx->final_ = false; + ctx->cluster_ = false; + return ""; + } + } else { + zcluster_object = (zval *) ctx->private_data; + // no available node, resolve again + sw_zend_call_method_with_0_params(zcluster_object, NULL, NULL, "count", &retval); + if (zval_get_long(&retval) == 0) { + ctx->dtor(ctx); + ctx->private_data = nullptr; + goto _lookup; + } + } + + sw_zend_call_method_with_0_params(zcluster_object, NULL, NULL, "pop", &retval); + if (!ZVAL_IS_ARRAY(&retval)) { + return ""; + } + zval *zhost = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL("host")); + if (zhost == nullptr || !ZVAL_IS_STRING(zhost)) { + return ""; + } + std::string result(Z_STRVAL_P(zhost), Z_STRLEN_P(zhost)); + if (ctx->with_port) { + result.append(":"); + zval *zport = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL("port")); + if (zport == nullptr) { + return ""; + } + result.append(std::to_string(zval_get_long(zport))); + } + zval_ptr_dtor(&retval); + return result; +} + +NameResolver::Context *php_swoole_name_resolver_get_context(zval *zobject) { + return swoole_name_resolver_context_get_handle(Z_OBJ_P(zobject)); +} diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc new file mode 100644 index 00000000000..a98701cc702 --- /dev/null +++ b/ext-src/swoole_odbc.cc @@ -0,0 +1,258 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_odbc.h" +#include "php_swoole_cxx.h" +#include "php_swoole_private.h" +#include "php_swoole_cxx.h" +#include "swoole_coroutine_system.h" + +#ifdef SW_USE_ODBC + +static SW_THREAD_LOCAL bool swoole_odbc_blocking = true; + +#ifdef SQL_ATTR_CONNECTION_POOLING +zend_ulong pdo_odbc_pool_on = SQL_CP_OFF; +zend_ulong pdo_odbc_pool_mode = SQL_CP_ONE_PER_HENV; +#endif + +void swoole_odbc_set_blocking(bool blocking) { + swoole_odbc_blocking = blocking; +} + +RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, + SQLSMALLINT NameLength1, + SQLCHAR *UserName, + SQLSMALLINT NameLength2, + SQLCHAR *Authentication, + SQLSMALLINT NameLength3) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLConnect(server=%s)", ServerName); + php_swoole_async(swoole_odbc_blocking, [&]() { + rc = SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDriverConnect"); + php_swoole_async(swoole_odbc_blocking, [&]() { + rc = SQLDriverConnect( + hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecDirect"); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecDirect(StatementHandle, StatementText, TextLength); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, + SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLGetInfo"); + rc = SQLGetInfo(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLength); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLGetInfo"); + rc = SQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPrepare(StatementText=%s)", StatementText); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPrepare(StatementHandle, StatementText, TextLength); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecute"); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecute(StatementHandle); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLCloseCursor"); + rc = SQLCloseCursor(StatementHandle); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPutData(StatementHandle, Data, StrLen_or_Ind); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); + php_swoole_async(swoole_odbc_blocking, [&]() { + rc = SQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLMoreResults(SQLHSTMT hstmt) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLMoreResults(hstmt); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, + SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, + SQLSMALLINT *Nullable) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); + php_swoole_async(swoole_odbc_blocking, [&]() { + rc = SQLDescribeCol(StatementHandle, + ColumnNumber, + ColumnName, + BufferLength, + NameLength, + DataType, + ColumnSize, + DecimalDigits, + Nullable); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLRowCount"); + rc = SQLRowCount(StatementHandle, RowCount); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLFreeHandle"); + rc = SQLFreeHandle(HandleType, Handle); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLEndTran(CompletionType=%d)", CompletionType); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLEndTran(HandleType, Handle, CompletionType); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDisconnect"); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLDisconnect(ConnectionHandle); }); + return rc; +} + +int php_swoole_odbc_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("ODBC_ATTR_USE_CURSOR_LIBRARY")) == + nullptr) { +#ifdef SQL_ATTR_CONNECTION_POOLING + const char *pooling_val = NULL; +#endif + +#ifdef SQL_ATTR_CONNECTION_POOLING + /* ugh, we don't really like .ini stuff in PDO, but since ODBC connection + * pooling is process wide, we can't set it from within the scope of a + * request without affecting others, which goes against our isolated request + * policy. So, we use cfg_get_string here to check it this once. + * */ + if (FAILURE == cfg_get_string("pdo_odbc.connection_pooling", (char **) &pooling_val) || pooling_val == NULL) { + pooling_val = "strict"; + } + if (strcasecmp(pooling_val, "strict") == 0 || strcmp(pooling_val, "1") == 0) { + pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV; + pdo_odbc_pool_mode = SQL_CP_STRICT_MATCH; + } else if (strcasecmp(pooling_val, "relaxed") == 0) { + pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV; + pdo_odbc_pool_mode = SQL_CP_RELAXED_MATCH; + } else if (*pooling_val == '\0' || strcasecmp(pooling_val, "off") == 0) { + pdo_odbc_pool_on = SQL_CP_OFF; + } else { + php_error_docref(NULL, + E_CORE_ERROR, + "Error in pdo_odbc.connection_pooling configuration. Value must be one of \"strict\", " + "\"relaxed\", or \"off\""); + return FAILURE; + } + + if (pdo_odbc_pool_on != SQL_CP_OFF) { + SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void *) pdo_odbc_pool_on, 0); + } +#endif + + REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_USE_CURSOR_LIBRARY", PDO_ODBC_ATTR_USE_CURSOR_LIBRARY); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_ASSUME_UTF8", PDO_ODBC_ATTR_ASSUME_UTF8); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_IF_NEEDED", SQL_CUR_USE_IF_NEEDED); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); + } + + php_pdo_unregister_driver(&swoole_pdo_odbc_driver); + php_pdo_register_driver(&swoole_pdo_odbc_driver); + + return SUCCESS; +} + +void php_swoole_odbc_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_odbc_driver); +} + +#endif diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc new file mode 100644 index 00000000000..d62c006050f --- /dev/null +++ b/ext-src/swoole_oracle.cc @@ -0,0 +1,152 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "php_swoole_private.h" +#include "php_swoole_cxx.h" +#include "swoole_coroutine.h" +#include "php_swoole_oracle.h" + +#ifdef SW_USE_ORACLE + +static SW_THREAD_LOCAL bool swoole_oracle_blocking = true; +void swoole_oracle_set_blocking(bool blocking) { + swoole_oracle_blocking = blocking; +} + +sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_session_begin"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); }); + + return result; +} + +sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_server_detach"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIServerDetach(srvhp, errhp, mode); }); + + return result; +} + +sword swoole_oci_stmt_prepare( + OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_prepare"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); + + return result; +} + +sword swoole_oci_stmt_execute(OCISvcCtx *svchp, + OCIStmt *stmtp, + OCIError *errhp, + ub4 iters, + ub4 rowoff, + const OCISnapshot *snap_in, + OCISnapshot *snap_out, + ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_execute"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); + + return result; +} + +sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); }); + + return result; +} + +sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch2"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); + + return result; +} + +sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_commit"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransCommit(svchp, errhp, flags); }); + + return result; +} + +sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_rollback"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransRollback(svchp, errhp, flags); }); + + return result; +} + +sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_ping"); + sword result = 0; + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIPing(svchp, errhp, mode); }); + + return result; +} + +const ub4 SWOOLE_PDO_OCI_INIT_MODE = OCI_DEFAULT | OCI_THREADED +#ifdef OCI_OBJECT + | OCI_OBJECT +#endif + ; + +OCIEnv *swoole_pdo_oci_Env = NULL; + +void php_swoole_oracle_rinit() { + if (!swoole_pdo_oci_Env) { +#ifdef HAVE_OCIENVCREATE + OCIEnvCreate(&swoole_pdo_oci_Env, SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL, 0, NULL); +#else + OCIInitialize(SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL); + OCIEnvInit(&swoole_pdo_oci_Env, OCI_DEFAULT, 0, NULL); +#endif + } +} + +void php_swoole_oracle_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("OCI_ATTR_ACTION")) == nullptr) { + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_ACTION", (zend_long) PDO_OCI_ATTR_ACTION); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CLIENT_INFO", (zend_long) PDO_OCI_ATTR_CLIENT_INFO); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CLIENT_IDENTIFIER", (zend_long) PDO_OCI_ATTR_CLIENT_IDENTIFIER); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_MODULE", (zend_long) PDO_OCI_ATTR_MODULE); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CALL_TIMEOUT", (zend_long) PDO_OCI_ATTR_CALL_TIMEOUT); + } + + php_pdo_unregister_driver(&swoole_pdo_oci_driver); + php_pdo_register_driver(&swoole_pdo_oci_driver); +} + +void php_swoole_oracle_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_oci_driver); + + if (!swoole_pdo_oci_Env) { + OCIHandleFree((dvoid *) swoole_pdo_oci_Env, OCI_HTYPE_ENV); + } +} +#endif diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc new file mode 100644 index 00000000000..a175526bd73 --- /dev/null +++ b/ext-src/swoole_pgsql.cc @@ -0,0 +1,236 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_pgsql.h" +#include "php_swoole_private.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" + +#ifdef SW_USE_PGSQL + +using swoole::Coroutine; +using swoole::Reactor; +using swoole::coroutine::Socket; +using swoole::coroutine::translate_events_to_poll; + +static SW_THREAD_LOCAL bool swoole_pgsql_blocking = true; + +static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1, bool check_nonblock = false) { + if (swoole_pgsql_blocking) { + struct pollfd fds[1]; + fds[0].fd = PQsocket(conn); + fds[0].events |= translate_events_to_poll(event); + + int result = 0; + do { + result = poll(fds, 1, timeout); + } while (result < 0 && errno == EINTR); + + return result > 0 ? 1 : errno == ETIMEDOUT ? 0 : -1; + } + + Socket sock(PQsocket(conn), SW_SOCK_RAW); + sock.get_socket()->nonblock = 1; + + bool retval = sock.poll(event, timeout); + while (check_nonblock && event == SW_EVENT_READ) { + if (PQconsumeInput(conn) == 0) { + retval = false; + break; + } + if (PQisBusy(conn) == 0) { + break; + } + retval = sock.poll(event, timeout); + } + + sock.move_fd(); + return retval ? 1 : sock.errCode == ETIMEDOUT ? 0 : -1; +} + +static int swoole_pgsql_flush(PGconn *conn) { + int flush_ret = -1; + + do { + int ret = swoole_pgsql_socket_poll(conn, SW_EVENT_WRITE); + if (sw_unlikely(ret < 0)) { + return -1; + } + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQflush(conn=%p)", conn); + flush_ret = PQflush(conn); + } while (flush_ret == 1); + + return flush_ret; +} + +static PGresult *swoole_pgsql_get_result(PGconn *conn) { + PGresult *result, *last_result = nullptr; + // PQgetResult will block the process; it is necessary to forcibly check if the data is ready. + int poll_ret = swoole_pgsql_socket_poll(conn, SW_EVENT_READ, -1, true); + if (sw_unlikely(poll_ret == SW_ERR)) { + return nullptr; + } + + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQgetResult(conn=%p)", conn); + while ((result = PQgetResult(conn))) { + PQclear(last_result); + last_result = result; + } + + return last_result; +} + +PGconn *swoole_pgsql_connectdb(const char *conninfo) { + PGconn *conn = PQconnectStart(conninfo); + if (conn == nullptr) { + return nullptr; + } + + int fd = PQsocket(conn); + if (sw_unlikely(fd < 0)) { + return conn; + } + + if (!swoole_pgsql_blocking && Coroutine::get_current()) { + PQsetnonblocking(conn, 1); + } else { + PQsetnonblocking(conn, 0); + } + + SW_LOOP { + int r = PQconnectPoll(conn); + if (r == PGRES_POLLING_OK || r == PGRES_POLLING_FAILED) { + break; + } + swEventType event; + + switch (r) { + case PGRES_POLLING_READING: + event = SW_EVENT_READ; + break; + case PGRES_POLLING_WRITING: + event = SW_EVENT_WRITE; + break; + default: + // should not be here including PGRES_POLLING_ACTIVE + abort(); + break; + } + + if (swoole_pgsql_socket_poll(conn, event) <= 0) { + break; + } + } + + return conn; +} + +PGresult *swoole_pgsql_prepare( + PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendPrepare(conn=%p, stmt_name='%s')", conn, stmt_name); + int ret = PQsendPrepare(conn, stmt_name, query, n_params, param_types); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec_prepared(PGconn *conn, + const char *stmt_name, + int n_params, + const char *const *param_values, + const int *param_lengths, + const int *param_formats, + int result_format) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryPrepared(conn=%p, stmt_name='%s')", conn, stmt_name); + int ret = PQsendQueryPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec(PGconn *conn, const char *query) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQuery(conn=%p, query='%s')", conn, query); + int ret = PQsendQuery(conn, query); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec_params(PGconn *conn, + const char *command, + int n_params, + const Oid *param_types, + const char *const *param_values, + const int *param_lengths, + const int *param_formats, + int result_format) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryParams(conn=%p, command='%s')", conn, command); + int ret = PQsendQueryParams( + conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +void swoole_pgsql_set_blocking(bool blocking) { + swoole_pgsql_blocking = blocking; +} + +void php_swoole_pgsql_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == + nullptr) { + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (zend_long) PGSQL_TRANSACTION_IDLE); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (zend_long) PGSQL_TRANSACTION_ACTIVE); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (zend_long) PGSQL_TRANSACTION_INTRANS); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long) PGSQL_TRANSACTION_INERROR); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long) PGSQL_TRANSACTION_UNKNOWN); + } + php_pdo_unregister_driver(&swoole_pdo_pgsql_driver); + php_pdo_register_driver(&swoole_pdo_pgsql_driver); +} + +void php_swoole_pgsql_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_pgsql_driver); +} + +#endif diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 636faccab29..a93bb50f4a1 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,13 +24,18 @@ #include #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_process_arginfo.h" +END_EXTERN_C() + using namespace swoole; zend_class_entry *swoole_process_ce; static zend_object_handlers swoole_process_handlers; -static uint32_t php_swoole_worker_round_id = 0; -static zend_fcall_info_cache *signal_fci_caches[SW_SIGNO_MAX] = {}; +static uint32_t round_process_id = 0; +static thread_local uint32_t server_user_worker_id = 0; +static zend::Callable *signal_fci_caches[SW_SIGNO_MAX] = {}; struct ProcessObject { Worker *worker; @@ -41,14 +46,18 @@ static sw_inline ProcessObject *php_swoole_process_fetch_object(zend_object *obj return (ProcessObject *) ((char *) obj - swoole_process_handlers.offset); } +static sw_inline ProcessObject *php_swoole_process_fetch_object(zval *zobj) { + return php_swoole_process_fetch_object(Z_OBJ_P(zobj)); +} + Worker *php_swoole_process_get_worker(zval *zobject) { return php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker; } Worker *php_swoole_process_get_and_check_worker(zval *zobject) { Worker *worker = php_swoole_process_get_worker(zobject); - if (!worker) { - php_swoole_fatal_error(E_ERROR, "you must call Process constructor first"); + if (UNEXPECTED(!worker)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return worker; } @@ -63,19 +72,17 @@ static void php_swoole_process_free_object(zend_object *object) { if (worker) { UnixSocket *_pipe = worker->pipe_object; - if (_pipe) { + if (_pipe && !worker->shared) { delete _pipe; } - if (worker->queue) { delete worker->queue; } - zend::Process *proc = (zend::Process *) worker->ptr2; if (proc) { delete proc; } - efree(worker); + delete worker; } zend_object_std_dtor(object); @@ -104,6 +111,7 @@ static PHP_METHOD(swoole_process, wait); static PHP_METHOD(swoole_process, daemon); #ifdef HAVE_CPU_AFFINITY static PHP_METHOD(swoole_process, setAffinity); +static PHP_METHOD(swoole_process, getAffinity); #endif static PHP_METHOD(swoole_process, set); static PHP_METHOD(swoole_process, setTimeout); @@ -122,142 +130,45 @@ SW_EXTERN_C_END static void php_swoole_onSignal(int signo); // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_construct, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_INFO(0, redirect_stdin_and_stdout) - ZEND_ARG_INFO(0, pipe_type) - ZEND_ARG_INFO(0, enable_coroutine) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_wait, 0, 0, 0) - ZEND_ARG_INFO(0, blocking) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_signal, 0, 0, 2) - ZEND_ARG_INFO(0, signal_no) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_alarm, 0, 0, 1) - ZEND_ARG_INFO(0, usec) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_kill, 0, 0, 1) - ZEND_ARG_INFO(0, pid) - ZEND_ARG_INFO(0, signal_no) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_daemon, 0, 0, 0) - ZEND_ARG_INFO(0, nochdir) - ZEND_ARG_INFO(0, noclose) - ZEND_ARG_INFO(0, pipes) -ZEND_END_ARG_INFO() - -#ifdef HAVE_CPU_AFFINITY -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setAffinity, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, cpu_settings, 0) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setPriority, 0, 0, 2) - ZEND_ARG_INFO(0, which) - ZEND_ARG_INFO(0, priority) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_getPriority, 0, 0, 1) - ZEND_ARG_INFO(0, which) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setTimeout, 0, 0, 1) - ZEND_ARG_INFO(0, seconds) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setBlocking, 0, 0, 1) - ZEND_ARG_INFO(0, blocking) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_useQueue, 0, 0, 0) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, mode) - ZEND_ARG_INFO(0, capacity) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_write, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_read, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pop, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_exit, 0, 0, 0) - ZEND_ARG_INFO(0, exit_code) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_exec, 0, 0, 2) - ZEND_ARG_INFO(0, exec_file) - ZEND_ARG_INFO(0, args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_name, 0, 0, 1) - ZEND_ARG_INFO(0, process_name) -ZEND_END_ARG_INFO() - #define MSGQUEUE_NOWAIT (1 << 8) static const zend_function_entry swoole_process_methods[] = { - PHP_ME(swoole_process, __construct, arginfo_swoole_process_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, __destruct, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, wait, arginfo_swoole_process_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, signal, arginfo_swoole_process_signal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, alarm, arginfo_swoole_process_alarm, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, kill, arginfo_swoole_process_kill, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, daemon, arginfo_swoole_process_daemon, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, __construct, arginfo_class_Swoole_Process___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, __destruct, arginfo_class_Swoole_Process___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, wait, arginfo_class_Swoole_Process_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, signal, arginfo_class_Swoole_Process_signal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, alarm, arginfo_class_Swoole_Process_alarm, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, kill, arginfo_class_Swoole_Process_kill, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, daemon, arginfo_class_Swoole_Process_daemon, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #ifdef HAVE_CPU_AFFINITY - PHP_ME(swoole_process, setAffinity, arginfo_swoole_process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, setAffinity, arginfo_class_Swoole_Process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, getAffinity, arginfo_class_Swoole_Process_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #endif - PHP_ME(swoole_process, setPriority, arginfo_swoole_process_setPriority, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, getPriority, arginfo_swoole_process_getPriority, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, set, arginfo_swoole_process_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, setTimeout, arginfo_swoole_process_setTimeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, setBlocking, arginfo_swoole_process_setBlocking, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, useQueue, arginfo_swoole_process_useQueue, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, statQueue, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, freeQueue, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, start, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, write, arginfo_swoole_process_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, close, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, read, arginfo_swoole_process_read, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, push, arginfo_swoole_process_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, pop, arginfo_swoole_process_pop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exit, arginfo_swoole_process_exit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exec, arginfo_swoole_process_exec, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exportSocket, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_FALIAS(name, swoole_set_process_name, arginfo_swoole_process_name) + PHP_ME(swoole_process, setPriority, arginfo_class_Swoole_Process_setPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, getPriority, arginfo_class_Swoole_Process_getPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, set, arginfo_class_Swoole_Process_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, setTimeout, arginfo_class_Swoole_Process_setTimeout, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, setBlocking, arginfo_class_Swoole_Process_setBlocking, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, useQueue, arginfo_class_Swoole_Process_useQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, statQueue, arginfo_class_Swoole_Process_statQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, freeQueue, arginfo_class_Swoole_Process_freeQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, start, arginfo_class_Swoole_Process_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, write, arginfo_class_Swoole_Process_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, close, arginfo_class_Swoole_Process_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, read, arginfo_class_Swoole_Process_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, push, arginfo_class_Swoole_Process_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, pop, arginfo_class_Swoole_Process_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exit, arginfo_class_Swoole_Process_exit, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exec, arginfo_class_Swoole_Process_exec, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exportSocket, arginfo_class_Swoole_Process_exportSocket, ZEND_ACC_PUBLIC) + PHP_FALIAS(name, swoole_set_process_name, arginfo_class_Swoole_Process_name) PHP_FE_END }; // clang-format on void php_swoole_process_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_process, "Swoole\\Process", "swoole_process", nullptr, swoole_process_methods); + SW_INIT_CLASS_ENTRY(swoole_process, "Swoole\\Process", nullptr, swoole_process_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process); SW_SET_CLASS_CLONEABLE(swoole_process, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process, sw_zend_class_unset_property_deny); @@ -325,28 +236,33 @@ void php_swoole_process_minit(int module_number) { REGISTER_LONG_CONSTANT("PRIO_PGRP", (zend_long) PRIO_PGRP, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PRIO_USER", (zend_long) PRIO_USER, CONST_CS | CONST_PERSISTENT); } + + SW_REGISTER_LONG_CONSTANT("SWOOLE_MSGQUEUE_ORIENT", SW_MSGQUEUE_ORIENT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_MSGQUEUE_BALANCE", SW_MSGQUEUE_BALANCE); } static PHP_METHOD(swoole_process, __construct) { - Worker *process = php_swoole_process_get_worker(ZEND_THIS); + auto po = php_swoole_process_fetch_object(ZEND_THIS); + Server *server = sw_server(); - if (process) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + if (po->worker) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } // only cli env if (!SWOOLE_G(cli)) { - php_swoole_fatal_error(E_ERROR, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } - if (sw_server() && sw_server()->is_started() && sw_server()->is_master()) { - php_swoole_fatal_error(E_ERROR, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + if (server && server->is_started() && server->is_master()) { + zend_throw_error(NULL, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } if (SwooleTG.async_threads) { - php_swoole_fatal_error(E_ERROR, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } @@ -355,6 +271,9 @@ static PHP_METHOD(swoole_process, __construct) { zend_long pipe_type = zend::PIPE_TYPE_DGRAM; zend_bool enable_coroutine = false; + po->worker = new Worker(); + Worker *process = po->worker; + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_FUNC(func.fci, func.fci_cache); Z_PARAM_OPTIONAL @@ -363,45 +282,60 @@ static PHP_METHOD(swoole_process, __construct) { Z_PARAM_BOOL(enable_coroutine) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - process = (Worker *) ecalloc(1, sizeof(Worker)); - - uint32_t base = 1; - if (sw_server() && sw_server()->is_started()) { - base = sw_server()->worker_num + sw_server()->task_worker_num + sw_server()->get_user_worker_num(); - } - if (php_swoole_worker_round_id == 0) { - php_swoole_worker_round_id = base; - } - process->id = php_swoole_worker_round_id++; - - if (redirect_stdin_and_stdout) { - process->redirect_stdin = true; - process->redirect_stdout = true; - process->redirect_stderr = true; - /** - * Forced to use stream pipe - */ - pipe_type = zend::PIPE_TYPE_STREAM; - } + if (server && server->is_worker_thread()) { + Worker *shared_worker; + if (server->is_user_worker()) { + shared_worker = server->get_worker(swoole_get_process_id()); + } else { + shared_worker = server->get_worker((server_user_worker_id++) + server->get_core_worker_num()); + } + *process = *shared_worker; + process->shared = true; + if (server->is_user_worker()) { + process->pipe_current = process->pipe_worker; + } else { + process->pipe_current = process->pipe_master; + } + } else { + if (redirect_stdin_and_stdout) { + process->redirect_stdin = true; + process->redirect_stdout = true; + process->redirect_stderr = true; + /** + * Forced to use stream pipe + */ + pipe_type = zend::PIPE_TYPE_STREAM; + } - if (pipe_type > 0) { - int socket_type = pipe_type == zend::PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM; - UnixSocket *_pipe = new UnixSocket(true, socket_type); - if (!_pipe->ready()) { - zend_throw_exception(swoole_exception_ce, "failed to create unix soccket", errno); - delete _pipe; - efree(process); - RETURN_FALSE; + uint32_t base = 1; + if (server && server->is_started()) { + base = server->get_all_worker_num(); } + if (round_process_id == 0) { + round_process_id = base; + } + process->id = round_process_id++; + process->shared = false; + + if (pipe_type > 0) { + int socket_type = pipe_type == zend::PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM; + UnixSocket *_pipe = new UnixSocket(true, socket_type); + if (!_pipe->ready()) { + zend_throw_exception(swoole_exception_ce, "failed to create unix soccket", errno); + delete _pipe; + efree(process); + RETURN_FALSE; + } - process->pipe_master = _pipe->get_socket(true); - process->pipe_worker = _pipe->get_socket(false); + process->pipe_master = _pipe->get_socket(true); + process->pipe_worker = _pipe->get_socket(false); - process->pipe_object = _pipe; - process->pipe_current = process->pipe_master; + process->pipe_object = _pipe; + process->pipe_current = process->pipe_master; - zend_update_property_long( - swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("pipe"), process->pipe_master->fd); + zend_update_property_long( + swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("pipe"), process->pipe_master->fd); + } } zend::Process *proc = new zend::Process((enum zend::PipeType) pipe_type, enable_coroutine); @@ -434,7 +368,7 @@ static PHP_METHOD(swoole_process, wait) { static PHP_METHOD(swoole_process, useQueue) { long msgkey = 0; - long mode = 2; + long mode = SW_MSGQUEUE_BALANCE; long capacity = -1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lll", &msgkey, &mode, &capacity) == FAILURE) { @@ -460,7 +394,7 @@ static PHP_METHOD(swoole_process, useQueue) { queue->set_capacity(capacity); } process->queue = queue; - process->ipc_mode = mode; + process->msgqueue_mode = mode; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("msgQueueId"), queue->get_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("msgQueueKey"), msgkey); RETURN_TRUE; @@ -514,9 +448,10 @@ static PHP_METHOD(swoole_process, kill) { } static PHP_METHOD(swoole_process, signal) { + SW_MUST_BE_MAIN_THREAD(); zend_long signo = 0; zval *zcallback = nullptr; - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *fci_cache = nullptr; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_LONG(signo) @@ -544,9 +479,13 @@ static PHP_METHOD(swoole_process, signal) { if (zcallback == nullptr) { fci_cache = signal_fci_caches[signo]; if (fci_cache) { +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); +#else swoole_signal_set(signo, nullptr); +#endif signal_fci_caches[signo] = nullptr; - swoole_event_defer(sw_zend_fci_cache_free, fci_cache); + swoole_event_defer(sw_callable_free, fci_cache); SwooleTG.signal_listener_num--; RETURN_TRUE; } else { @@ -556,33 +495,29 @@ static PHP_METHOD(swoole_process, signal) { } else if (Z_TYPE_P(zcallback) == IS_LONG && Z_LVAL_P(zcallback) == (zend_long) SIG_IGN) { handler = nullptr; } else { - char *func_name; - fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, &func_name, 0, fci_cache, nullptr)) { - php_swoole_error(E_WARNING, "function '%s' is not callable", func_name); - efree(func_name); - efree(fci_cache); + fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { RETURN_FALSE; } - efree(func_name); - sw_zend_fci_cache_persist(fci_cache); handler = php_swoole_onSignal; } if (sw_server() && sw_server()->is_sync_process()) { if (signal_fci_caches[signo]) { - sw_zend_fci_cache_free(signal_fci_caches[signo]); + sw_callable_free(signal_fci_caches[signo]); } else { SwooleTG.signal_listener_num++; } signal_fci_caches[signo] = fci_cache; +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler); }, nullptr); +#else swoole_signal_set(signo, handler); +#endif RETURN_TRUE; } php_swoole_check_reactor(); - // for swSignalfd_setup - SwooleTG.reactor->check_signalfd = true; if (!SwooleTG.reactor->isset_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER)) { SwooleTG.reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER, [](Reactor *reactor, size_t &event_num) -> bool { @@ -592,16 +527,17 @@ static PHP_METHOD(swoole_process, signal) { if (signal_fci_caches[signo]) { // free the old fci_cache - swoole_event_defer(sw_zend_fci_cache_free, signal_fci_caches[signo]); + swoole_event_defer(sw_callable_free, signal_fci_caches[signo]); } else { SwooleTG.signal_listener_num++; } signal_fci_caches[signo] = fci_cache; - // use user settings - SwooleG.use_signalfd = SwooleG.enable_signalfd; - +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler); }, nullptr); +#else swoole_signal_set(signo, handler); +#endif RETURN_TRUE; } @@ -654,13 +590,13 @@ static PHP_METHOD(swoole_process, alarm) { * safe signal */ static void php_swoole_onSignal(int signo) { - zend_fcall_info_cache *fci_cache = signal_fci_caches[signo]; + auto fci_cache = signal_fci_caches[signo]; if (fci_cache) { zval argv[1]; ZVAL_LONG(&argv[0], signo); - if (UNEXPECTED(!zend::function::call(fci_cache, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error( E_WARNING, "%s: signal [%d] handler error", ZSTR_VAL(swoole_process_ce->name), signo); } @@ -677,25 +613,27 @@ zend_bool php_swoole_signal_isset_handler(int signo) { void php_swoole_process_clean() { for (int i = 0; i < SW_SIGNO_MAX; i++) { - zend_fcall_info_cache *fci_cache = signal_fci_caches[i]; + auto fci_cache = signal_fci_caches[i]; if (fci_cache) { - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + sw_callable_free(fci_cache); signal_fci_caches[i] = nullptr; } } - - if (SwooleG.process_type != SW_PROCESS_USERWORKER) { - SwooleG.process_type = 0; +#ifndef SW_THREAD + if (swoole_get_process_type() != SW_PROCESS_USERWORKER) { + swoole_set_process_type(0); } +#endif +} + +void php_swoole_process_rshutdown() { + php_swoole_process_clean(); } int php_swoole_process_start(Worker *process, zval *zobject) { zval *zcallback = sw_zend_read_property_ex(swoole_process_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_CALLBACK), 0); - zend_fcall_info_cache fci_cache; - - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, nullptr, 0, &fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "Illegal callback function of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { return SW_ERR; } @@ -723,7 +661,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { } php_swoole_process_clean(); - SwooleG.process_id = process->id; + swoole_set_process_id(process->id); SwooleWG.worker = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); @@ -736,15 +674,16 @@ int php_swoole_process_start(Worker *process, zval *zobject) { return SW_ERR; } // main function - if (UNEXPECTED(!zend::function::call(&fci_cache, 1, zobject, nullptr, proc->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, zobject, nullptr, proc->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } // eventloop start if (proc->enable_coroutine) { php_swoole_event_wait(); } + sw_callable_free(fci_cache); // equivalent to exit - sw_zend_bailout(); + zend_bailout(); return SW_OK; } @@ -780,10 +719,6 @@ static PHP_METHOD(swoole_process, read) { RETURN_FALSE; } - if (buf_size > 65536) { - buf_size = 65536; - } - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); if (process->pipe_current == nullptr) { @@ -927,7 +862,7 @@ static PHP_METHOD(swoole_process, pop) { char data[SW_MSGMAX]; } message; - if (process->ipc_mode == 2) { + if (process->msgqueue_mode == SW_MSGQUEUE_BALANCE) { message.type = 0; } else { message.type = process->id + 1; @@ -1013,37 +948,67 @@ static PHP_METHOD(swoole_process, daemon) { } #ifdef HAVE_CPU_AFFINITY -static PHP_METHOD(swoole_process, setAffinity) { - zval *array; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) { - RETURN_FALSE; - } +bool php_swoole_array_to_cpu_set(zval *array, cpu_set_t *cpu_set) { if (php_swoole_array_length(array) == 0) { - RETURN_FALSE; + return false; } + if (php_swoole_array_length(array) > SW_CPU_NUM) { php_swoole_fatal_error(E_WARNING, "More than the number of CPU"); - RETURN_FALSE; + return false; } zval *value = nullptr; - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); + CPU_ZERO(cpu_set); SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(array), value) if (zval_get_long(value) >= SW_CPU_NUM) { php_swoole_fatal_error(E_WARNING, "invalid cpu id [%d]", (int) Z_LVAL_P(value)); - RETURN_FALSE; + return false; } - CPU_SET(Z_LVAL_P(value), &cpu_set); + CPU_SET(Z_LVAL_P(value), cpu_set); SW_HASHTABLE_FOREACH_END(); + return true; +} + +void php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set) { + array_init(array); + + int cpu_n = SW_CPU_NUM; + SW_LOOP_N(cpu_n) { + if (CPU_ISSET(i, cpu_set)) { + add_next_index_long(array, i); + } + } +} + +static PHP_METHOD(swoole_process, setAffinity) { + zval *array; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END(); + + cpu_set_t cpu_set; + if (!php_swoole_array_to_cpu_set(array, &cpu_set)) { + RETURN_FALSE; + } + if (swoole_set_cpu_affinity(&cpu_set) < 0) { php_swoole_sys_error(E_WARNING, "sched_setaffinity() failed"); RETURN_FALSE; } RETURN_TRUE; } + +static PHP_METHOD(swoole_process, getAffinity) { + cpu_set_t cpu_set; + if (swoole_get_cpu_affinity(&cpu_set) < 0) { + php_swoole_sys_error(E_WARNING, "sched_getaffinity() failed"); + RETURN_FALSE; + } + php_swoole_cpu_set_to_array(return_value, &cpu_set); +} #endif static PHP_METHOD(swoole_process, exit) { @@ -1065,6 +1030,10 @@ static PHP_METHOD(swoole_process, exit) { ret_code = 1; } + if (swoole_event_is_available()) { + swoole_event_free(); + } + exit(ret_code); } @@ -1156,23 +1125,55 @@ static PHP_METHOD(swoole_process, setBlocking) { } } +#define SW_CHECK_PRIORITY_WHO() \ + if (who_is_null) { \ + if (which == PRIO_PROCESS) { \ + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); \ + who = process->pid; \ + } else { \ + php_swoole_fatal_error(E_WARNING, "$who parameter must not be null"); \ + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); \ + RETURN_FALSE; \ + } \ + } + static PHP_METHOD(swoole_process, setPriority) { - zend_long which, priority; - ZEND_PARSE_PARAMETERS_START(2, 2) + zend_long which, priority, who; + bool who_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_LONG(which) Z_PARAM_LONG(priority) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(who, who_is_null) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - RETURN_BOOL(setpriority(which, process->pid, priority) == 0); + SW_CHECK_PRIORITY_WHO(); + if (setpriority(which, who, priority) < 0) { + swoole_set_last_error(errno); + RETURN_FALSE; + } else { + RETURN_TRUE; + } } static PHP_METHOD(swoole_process, getPriority) { - zend_long which; - ZEND_PARSE_PARAMETERS_START(1, 1) + zend_long which, who; + bool who_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_LONG(which) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(who, who_is_null) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - RETURN_LONG(getpriority(which, process->pid)); + SW_CHECK_PRIORITY_WHO(); + errno = 0; + int priority = getpriority(which, who); + if (priority == -1 && errno != 0) { + swoole_set_last_error(errno); + RETURN_FALSE; + } else { + RETURN_LONG(priority); + } } diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 70e897b2024..fcf02d3a94c 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -20,104 +20,90 @@ #include "swoole_server.h" #include "swoole_signal.h" -using namespace swoole; +BEGIN_EXTERN_C() +#include "stubs/php_swoole_process_pool_arginfo.h" +END_EXTERN_C() -struct ProcessPoolProperty { - zend_fcall_info_cache *onStart; - zend_fcall_info_cache *onWorkerStart; - zend_fcall_info_cache *onWorkerStop; - zend_fcall_info_cache *onMessage; - bool enable_coroutine; -}; +using namespace swoole; static zend_class_entry *swoole_process_pool_ce; static zend_object_handlers swoole_process_pool_handlers; -static ProcessPool *current_pool; +static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; struct ProcessPoolObject { ProcessPool *pool; - ProcessPoolProperty *pp; + zend::Callable *onStart; + zend::Callable *onShutdown; + zend::Callable *onWorkerStart; + zend::Callable *onWorkerStop; + zend::Callable *onWorkerExit; + zend::Callable *onMessage; + zend_bool enable_coroutine; + zend_bool enable_message_bus; zend_object std; }; -static void pool_signal_handler(int sig); +static void process_pool_signal_handler(int sig); -static sw_inline ProcessPoolObject *php_swoole_process_pool_fetch_object(zend_object *obj) { +static sw_inline ProcessPoolObject *process_pool_fetch_object(zend_object *obj) { return (ProcessPoolObject *) ((char *) obj - swoole_process_pool_handlers.offset); } -static sw_inline ProcessPool *php_swoole_process_pool_get_pool(zval *zobject) { - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool; -} - -static sw_inline ProcessPool *php_swoole_process_pool_get_and_check_pool(zval *zobject) { - ProcessPool *pool = php_swoole_process_pool_get_pool(zobject); - if (!pool) { - php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); - } - return pool; +static sw_inline ProcessPoolObject *process_pool_fetch_object(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject)); } -static sw_inline void php_swoole_process_pool_set_pool(zval *zobject, ProcessPool *pool) { - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool = pool; +static sw_inline ProcessPool *process_pool_get_pool(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject))->pool; } -static sw_inline ProcessPoolProperty *php_swoole_process_pool_get_pp(zval *zobject) { - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp; -} - -static sw_inline ProcessPoolProperty *php_swoole_process_pool_get_and_check_pp(zval *zobject) { - ProcessPoolProperty *pp = php_swoole_process_pool_get_pp(zobject); - if (!pp) { - php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); +static sw_inline ProcessPool *process_pool_get_and_check_pool(zval *zobject) { + ProcessPool *pool = process_pool_get_pool(zobject); + if (UNEXPECTED(!pool)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } - return pp; -} - -static sw_inline void php_swoole_process_pool_set_pp(zval *zobject, ProcessPoolProperty *pp) { - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp = pp; + return pool; } -static void php_swoole_process_pool_free_object(zend_object *object) { - ProcessPoolObject *process_pool = php_swoole_process_pool_fetch_object(object); +static void process_pool_free_object(zend_object *object) { + ProcessPoolObject *pp = process_pool_fetch_object(object); - ProcessPool *pool = process_pool->pool; + ProcessPool *pool = pp->pool; if (pool) { efree(pool->ptr); pool->destroy(); efree(pool); } - ProcessPoolProperty *pp = process_pool->pp; - if (pp) { - if (pp->onWorkerStart) { - sw_zend_fci_cache_discard(pp->onWorkerStart); - efree(pp->onWorkerStart); - } - if (pp->onMessage) { - sw_zend_fci_cache_discard(pp->onMessage); - efree(pp->onMessage); - } - if (pp->onWorkerStop) { - sw_zend_fci_cache_discard(pp->onWorkerStop); - efree(pp->onWorkerStop); - } - if (pp->onStart) { - sw_zend_fci_cache_discard(pp->onStart); - efree(pp->onStart); - } - efree(pp); + if (pp->onWorkerStart) { + sw_callable_free(pp->onWorkerStart); + } + if (pp->onMessage) { + sw_callable_free(pp->onMessage); + } + if (pp->onWorkerStop) { + sw_callable_free(pp->onWorkerStop); + } + if (pp->onStart) { + sw_callable_free(pp->onStart); + } + if (pp->onWorkerExit) { + sw_callable_free(pp->onWorkerExit); + } + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); } zend_object_std_dtor(object); } -static zend_object *php_swoole_process_pool_create_object(zend_class_entry *ce) { - ProcessPoolObject *process_pool = (ProcessPoolObject *) zend_object_alloc(sizeof(ProcessPoolObject), ce); - zend_object_std_init(&process_pool->std, ce); - object_properties_init(&process_pool->std, ce); - process_pool->std.handlers = &swoole_process_pool_handlers; - return &process_pool->std; +static zend_object *process_pool_create_object(zend_class_entry *ce) { + ProcessPoolObject *pp = (ProcessPoolObject *) zend_object_alloc(sizeof(ProcessPoolObject), ce); + zend_object_std_init(&pp->std, ce); + object_properties_init(&pp->std, ce); + pp->std.handlers = &swoole_process_pool_handlers; + return &pp->std; } SW_EXTERN_C_BEGIN @@ -127,6 +113,7 @@ static PHP_METHOD(swoole_process_pool, set); static PHP_METHOD(swoole_process_pool, on); static PHP_METHOD(swoole_process_pool, listen); static PHP_METHOD(swoole_process_pool, write); +static PHP_METHOD(swoole_process_pool, sendMessage); static PHP_METHOD(swoole_process_pool, detach); static PHP_METHOD(swoole_process_pool, getProcess); static PHP_METHOD(swoole_process_pool, start); @@ -135,142 +122,188 @@ static PHP_METHOD(swoole_process_pool, shutdown); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_construct, 0, 0, 1) - ZEND_ARG_INFO(0, worker_num) - ZEND_ARG_INFO(0, ipc_type) - ZEND_ARG_INFO(0, msgqueue_key) - ZEND_ARG_INFO(0, enable_coroutine) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_getProcess, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_listen, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, backlog) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_write, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_process_pool_methods[] = { - PHP_ME(swoole_process_pool, __construct, arginfo_swoole_process_pool_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, __destruct, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, set, arginfo_swoole_process_pool_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, on, arginfo_swoole_process_pool_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, getProcess, arginfo_swoole_process_pool_getProcess, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, listen, arginfo_swoole_process_pool_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, write, arginfo_swoole_process_pool_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, detach, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, start, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, stop, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, shutdown, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, __construct, arginfo_class_Swoole_Process_Pool___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, __destruct, arginfo_class_Swoole_Process_Pool___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, set, arginfo_class_Swoole_Process_Pool_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, on, arginfo_class_Swoole_Process_Pool_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, getProcess, arginfo_class_Swoole_Process_Pool_getProcess, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, listen, arginfo_class_Swoole_Process_Pool_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, write, arginfo_class_Swoole_Process_Pool_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, sendMessage, arginfo_class_Swoole_Process_Pool_sendMessage, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, detach, arginfo_class_Swoole_Process_Pool_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, start, arginfo_class_Swoole_Process_Pool_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, stop, arginfo_class_Swoole_Process_Pool_stop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, shutdown, arginfo_class_Swoole_Process_Pool_shutdown, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_process_pool_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_process_pool, "Swoole\\Process\\Pool", "swoole_process_pool", nullptr, swoole_process_pool_methods); + SW_INIT_CLASS_ENTRY(swoole_process_pool, "Swoole\\Process\\Pool", nullptr, swoole_process_pool_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process_pool); SW_SET_CLASS_CLONEABLE(swoole_process_pool, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process_pool, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_process_pool, - php_swoole_process_pool_create_object, - php_swoole_process_pool_free_object, - ProcessPoolObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_process_pool, process_pool_create_object, process_pool_free_object, ProcessPoolObject, std); zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("master_pid"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("workerPid"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("workerId"), -1, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_process_pool_ce, ZEND_STRL("workers"), ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL("workerRunning"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL("running"), -1, ZEND_ACC_PUBLIC); } -static void pool_onWorkerStart(ProcessPool *pool, int worker_id) { +static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); - + ProcessPoolObject *pp = process_pool_fetch_object(zobject); php_swoole_process_clean(); - SwooleG.process_id = worker_id; + current_pool = pool; - // main function - if (!pp->onWorkerStart) { - return; - } - // eventloop create - if (pp->enable_coroutine && php_swoole_reactor_init() < 0) { - return; + current_worker = worker; + + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), true); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), true); + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerPid"), getpid()); + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerId"), worker->id); + + swoole_set_process_type(SW_PROCESS_WORKER); + SwooleG.enable_coroutine = pp->enable_coroutine; + + if (pp->onWorkerStart) { + zval args[2]; + args[0] = *zobject; + ZVAL_LONG(&args[1], worker->id); + if (UNEXPECTED(!zend::function::call(pp->onWorkerStart->ptr(), 2, args, nullptr, pp->enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } } - if (!pp->enable_coroutine && pp->onMessage) { - swoole_signal_set(SIGTERM, pool_signal_handler); + + if (!swoole_signal_isset(SIGTERM) && (pp->onMessage || pp->enable_coroutine)) { + swoole_signal_set(SIGTERM, process_pool_signal_handler); } +} + +static void process_pool_onMessage(ProcessPool *pool, RecvData *msg) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; + args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStart, 2, args, nullptr, pp->enable_coroutine))) { - php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + const char *data = msg->data; + uint32_t length = msg->info.len; + if (length == 0) { + ZVAL_EMPTY_STRING(&args[1]); + } else { + if (msg->info.flags & SW_EVENT_DATA_OBJ_PTR) { + zend::assign_zend_string_by_val(&args[1], (char *) data, length); + pool->message_bus->move_packet(); + } else { + ZVAL_STRINGL(&args[1], data, length); + } } - // eventloop start - if (pp->enable_coroutine) { - php_swoole_event_wait(); + auto *worker = sw_worker(); + worker->set_status_to_busy(); + if (UNEXPECTED(!zend::function::call(pp->onMessage->ptr(), 2, args, nullptr, pp->enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } + worker->add_request_count(); + worker->set_status_to_idle(); + zval_ptr_dtor(&args[1]); } -static void pool_onMessage(ProcessPool *pool, const char *data, uint32_t length) { +static void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); + ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; - args[0] = *zobject; - ZVAL_STRINGL(&args[1], data, length); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); - if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + if (pp->onWorkerStop == nullptr) { + return; } - zval_ptr_dtor(&args[1]); + args[0] = *zobject; + ZVAL_LONG(&args[1], worker->id); + + if (UNEXPECTED(!zend::function::call(pp->onWorkerStop->ptr(), 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } } -static void pool_onWorkerStop(ProcessPool *pool, int worker_id) { +static void process_pool_onWorkerExit(ProcessPool *pool, Worker *worker) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); + ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; - if (pp->onWorkerStop == nullptr) { + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); + + if (pp->onWorkerExit == nullptr) { return; } args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStop, 2, args, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + if (UNEXPECTED(!zend::function::call(pp->onWorkerExit->ptr(), 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onWorkerExit handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } +} + +static void process_pool_onStart(ProcessPool *pool) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[1]; + + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), true); + + swoole_set_process_type(SW_PROCESS_MASTER); + SwooleG.enable_coroutine = false; + + if (pp->onStart == nullptr) { + return; + } + + args[0] = *zobject; + if (UNEXPECTED(!zend::function::call(pp->onStart->ptr(), 1, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } +} + +static void process_pool_onShutdown(ProcessPool *pool) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[1]; + + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); + + if (pp->onShutdown == nullptr) { + return; + } + + args[0] = *zobject; + + if (UNEXPECTED(!zend::function::call(pp->onShutdown->ptr(), 1, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } } -static void pool_signal_handler(int sig) { +static void process_pool_signal_handler(int sig) { if (!current_pool) { return; } switch (sig) { case SIGTERM: current_pool->running = false; + if (current_worker) { + current_pool->stop(current_worker); + } break; case SIGUSR1: case SIGUSR2: @@ -285,6 +318,10 @@ static void pool_signal_handler(int sig) { } } +ProcessPool *sw_process_pool() { + return current_pool; +} + static PHP_METHOD(swoole_process_pool, __construct) { zval *zobject = ZEND_THIS; zend_long worker_num; @@ -294,30 +331,45 @@ static PHP_METHOD(swoole_process_pool, __construct) { // only cli env if (!SWOOLE_G(cli)) { - php_swoole_fatal_error(E_ERROR, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(zobject)); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(zobject)); RETURN_FALSE; } if (sw_server()) { - php_swoole_fatal_error(E_ERROR, "%s cannot use in server process", SW_Z_OBJCE_NAME_VAL_P(zobject)); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_error(NULL, "cannot create server and process pool instances simultaneously"); + RETURN_FALSE; + } + + if (sw_process_pool()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_error(NULL, "A process pool instance has already been created and cannot be created again"); RETURN_FALSE; } +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_exception_ex(swoole_exception_ce, -1, "This operation is only allowed in the main thread"); + RETURN_FALSE; + } +#endif + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l|llb", &worker_num, &ipc_type, &msgq_key, &enable_coroutine) == FAILURE) { RETURN_FALSE; } if (worker_num <= 0) { - zend_throw_exception_ex(swoole_exception_ce, errno, "invalid worker_num"); + zend_throw_exception_ex(swoole_exception_ce, errno, "the parameter $worker_num must be greater than 0"); RETURN_FALSE; } if (enable_coroutine && ipc_type > 0 && ipc_type != SW_IPC_UNIXSOCK) { ipc_type = SW_IPC_UNIXSOCK; - php_swoole_fatal_error(E_NOTICE, - "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", - SW_Z_OBJCE_NAME_VAL_P(zobject)); + zend_throw_error(NULL, "the parameter $ipc_type must be SWOOLE_IPC_UNIXSOCK when enable coroutine"); + RETURN_FALSE; } ProcessPool *pool = (ProcessPool *) emalloc(sizeof(*pool)); @@ -329,19 +381,12 @@ static PHP_METHOD(swoole_process_pool, __construct) { } pool->ptr = sw_zval_dup(zobject); + pool->async = enable_coroutine; - if (enable_coroutine) { - pool->main_loop = nullptr; - } else { - if (ipc_type > 0) { - pool->set_protocol(0, SW_INPUT_BUFFER_SIZE); - } - } + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); - ProcessPoolProperty *pp = (ProcessPoolProperty *) ecalloc(1, sizeof(ProcessPoolProperty)); pp->enable_coroutine = enable_coroutine; - php_swoole_process_pool_set_pp(zobject, pp); - php_swoole_process_pool_set_pool(zobject, pool); + pp->pool = pool; } static PHP_METHOD(swoole_process_pool, set) { @@ -355,30 +400,34 @@ static PHP_METHOD(swoole_process_pool, set) { vht = Z_ARRVAL_P(zset); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); php_swoole_set_global_option(vht); php_swoole_set_coroutine_option(vht); php_swoole_set_aio_option(vht); if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - pp->enable_coroutine = zval_is_true(ztmp); + pool->async = pp->enable_coroutine = zval_is_true(ztmp); } - - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - if (pp->enable_coroutine) { - pool->main_loop = nullptr; + if (php_swoole_array_get_value(vht, "enable_message_bus", ztmp)) { + pp->enable_message_bus = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "max_package_size", ztmp)) { + pool->set_max_packet_size(php_swoole_parse_to_size(ztmp)); + } + if (php_swoole_array_get_value(vht, "max_wait_time", ztmp)) { + zend_long v = zval_get_long(ztmp); + pool->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } } static PHP_METHOD(swoole_process_pool, on) { char *name; size_t l_name; + zval *zfn; - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; - - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->started) { php_swoole_fatal_error(E_WARNING, "process pool is started. unable to register event callback function"); @@ -387,63 +436,51 @@ static PHP_METHOD(swoole_process_pool, on) { ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) Z_PARAM_STRING(name, l_name) - Z_PARAM_FUNC(fci, fci_cache); + Z_PARAM_ZVAL(zfn); ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); if (SW_STRCASEEQ(name, l_name, "WorkerStart")) { if (pp->onWorkerStart) { - sw_zend_fci_cache_discard(pp->onWorkerStart); - efree(pp->onWorkerStart); - } else { - pp->onWorkerStart = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onWorkerStart); } - *pp->onWorkerStart = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStart); - RETURN_TRUE; + pp->onWorkerStart = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Message")) { - if (pp->enable_coroutine) { - php_swoole_fatal_error(E_NOTICE, "cannot set onMessage event with enable_coroutine"); - RETURN_FALSE; - } if (pool->ipc_mode == SW_IPC_NONE) { - php_swoole_fatal_error(E_WARNING, "cannot set onMessage event with ipc_type=0"); + zend_throw_exception( + swoole_exception_ce, "cannot set `onMessage` event with ipc_type=0", SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } if (pp->onMessage) { - sw_zend_fci_cache_discard(pp->onMessage); - efree(pp->onMessage); - } else { - pp->onMessage = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onMessage); } - *pp->onMessage = fci_cache; - sw_zend_fci_cache_persist(pp->onMessage); - RETURN_TRUE; + pp->onMessage = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "WorkerStop")) { if (pp->onWorkerStop) { - sw_zend_fci_cache_discard(pp->onWorkerStop); - efree(pp->onWorkerStop); - } else { - pp->onWorkerStop = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onWorkerStop); + } + pp->onWorkerStop = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "WorkerExit")) { + if (pp->onWorkerExit) { + sw_callable_free(pp->onWorkerExit); } - *pp->onWorkerStop = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStop); - RETURN_TRUE; + pp->onWorkerExit = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Start")) { if (pp->onStart) { - sw_zend_fci_cache_discard(pp->onStart); - efree(pp->onStart); - } else { - pp->onStart = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onStart); + } + pp->onStart = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Shutdown")) { + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); } - *pp->onStart = fci_cache; - sw_zend_fci_cache_persist(pp->onStart); - RETURN_TRUE; + pp->onShutdown = sw_callable_create(zfn); } else { php_swoole_error(E_WARNING, "unknown event type[%s]", name); RETURN_FALSE; } + RETURN_TRUE; } static PHP_METHOD(swoole_process_pool, listen) { @@ -452,7 +489,7 @@ static PHP_METHOD(swoole_process_pool, listen) { zend_long port = 0; zend_long backlog = 2048; - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->started) { php_swoole_fatal_error(E_WARNING, "process pool is started. unable to listen"); @@ -470,7 +507,7 @@ static PHP_METHOD(swoole_process_pool, listen) { int ret; // unix socket - if (SW_STRCASECT(host, l_host, "unix:/")) { + if (SW_STR_ISTARTS_WITH(host, l_host, "unix:/")) { ret = pool->listen(host + 5, backlog); } else { ret = pool->listen(host, port, backlog); @@ -484,11 +521,11 @@ static PHP_METHOD(swoole_process_pool, write) { char *data; size_t length; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(data, length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->ipc_mode != SW_IPC_SOCKET) { php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); RETURN_FALSE; @@ -499,56 +536,100 @@ static PHP_METHOD(swoole_process_pool, write) { SW_CHECK_RETURN(pool->response(data, length)); } -static PHP_METHOD(swoole_process_pool, start) { - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - if (pool->started) { - php_swoole_fatal_error(E_WARNING, "process pool is started. unable to execute swoole_process_pool->start"); +static PHP_METHOD(swoole_process_pool, sendMessage) { + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); + if (!pool->started) { + php_swoole_fatal_error(E_WARNING, "process pool is not started."); + RETURN_FALSE; + } + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); RETURN_FALSE; } - swoole_event_free(); + char *message; + size_t l_message; + zend_long worker_id; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(message, l_message) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Worker *worker = pool->get_worker(worker_id); + if (pool->message_bus) { + SendData _task{}; + _task.info.reactor_id = current_worker ? current_worker->pid : -1; + _task.info.len = l_message; + _task.data = message; + RETURN_BOOL(pool->message_bus->write(worker->pipe_master, &_task)); + } else { + RETURN_BOOL(worker->pipe_master->send_async(message, l_message)); + } +} - SwooleG.use_signalfd = 0; +static PHP_METHOD(swoole_process_pool, start) { + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); + if (pool->started) { + php_swoole_fatal_error(E_WARNING, "process pool is started"); + RETURN_FALSE; + } + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); std::unordered_map ori_handlers; - ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, pool_signal_handler); - ori_handlers[SIGUSR1] = swoole_signal_set(SIGUSR1, pool_signal_handler); - ori_handlers[SIGUSR2] = swoole_signal_set(SIGUSR2, pool_signal_handler); - ori_handlers[SIGIO] = swoole_signal_set(SIGIO, pool_signal_handler); + // The reactor must be cleaned up before registering signal + swoole_event_free(); + ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, process_pool_signal_handler); + ori_handlers[SIGUSR1] = swoole_signal_set(SIGUSR1, process_pool_signal_handler); + ori_handlers[SIGUSR2] = swoole_signal_set(SIGUSR2, process_pool_signal_handler); + ori_handlers[SIGIO] = swoole_signal_set(SIGIO, process_pool_signal_handler); - if (pool->ipc_mode == SW_IPC_NONE || pp->enable_coroutine) { - if (pp->onWorkerStart == nullptr) { - php_swoole_fatal_error(E_ERROR, "require onWorkerStart callback"); + if (pp->enable_message_bus) { + if (pool->create_message_bus() != SW_OK) { RETURN_FALSE; } + pool->message_bus->set_allocator(sw_zend_string_allocator()); + pool->set_protocol(SW_PROTOCOL_MESSAGE); } else { - if (pp->onMessage == nullptr) { - php_swoole_fatal_error(E_ERROR, "require onMessage callback"); + pool->set_protocol(SW_PROTOCOL_STREAM); + } + + if (pp->onWorkerStart == nullptr && pp->onMessage == nullptr) { + if (pool->async) { + php_swoole_fatal_error(E_ERROR, "require 'onWorkerStart' callback"); + RETURN_FALSE; + } else if (pool->ipc_mode != SW_IPC_NONE && pp->onMessage == nullptr) { + php_swoole_fatal_error(E_ERROR, "require 'onMessage' callback"); RETURN_FALSE; } - pool->onMessage = pool_onMessage; } - pool->onWorkerStart = pool_onWorkerStart; - pool->onWorkerStop = pool_onWorkerStop; - - zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("master_pid"), getpid()); - - if (pool->start() < 0) { + if (pp->onWorkerExit && !pp->enable_coroutine) { + zend_throw_exception( + swoole_exception_ce, "cannot set `onWorkerExit` without enable_coroutine", SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } + if (pp->onMessage) { + pool->onMessage = process_pool_onMessage; + } else { + pool->main_loop = nullptr; + } + current_pool = pool; - if (pp->onStart) { - zval args[1]; - args[0] = *ZEND_THIS; - if (UNEXPECTED(!zend::function::call(pp->onStart, 1, args, nullptr, 0))) { - php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } + pool->onStart = process_pool_onStart; + pool->onShutdown = process_pool_onShutdown; + pool->onWorkerStart = process_pool_onWorkerStart; + pool->onWorkerStop = process_pool_onWorkerStop; + + if (pp->enable_coroutine && pp->onWorkerExit) { + pool->onWorkerExit = process_pool_onWorkerExit; + } + + if (pool->start() < 0) { + RETURN_FALSE; } pool->wait(); @@ -583,7 +664,7 @@ static PHP_METHOD(swoole_process_pool, getProcess) { php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); RETURN_FALSE; } else if (worker_id < 0) { - worker_id = SwooleG.process_id; + worker_id = swoole_get_process_id(); } zval *zworkers = @@ -600,11 +681,11 @@ static PHP_METHOD(swoole_process_pool, getProcess) { *worker = current_pool->workers[worker_id]; object_init_ex(zprocess, swoole_process_ce); - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), SwooleG.process_id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), swoole_get_process_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pid"), worker->pid); if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) { // current process - if (worker->id == SwooleG.process_id) { + if (worker->id == swoole_get_process_id()) { worker->pipe_current = worker->pipe_worker; } else { worker->pipe_current = worker->pipe_master; @@ -616,9 +697,22 @@ static PHP_METHOD(swoole_process_pool, getProcess) { zend_update_property_long( swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pipe"), worker->pipe_current->fd); } + /** + * The message bus is enabled and forbid to read/write/close the pipeline in the php layer + */ + if (current_pool->message_bus) { + worker->pipe_current = nullptr; + worker->pipe_object = nullptr; + } + /** + * The onMessage callback is not set, use getProcess()->push()/pop() to operate msgqueue + */ + if (current_pool->ipc_mode == SW_IPC_MSGQUEUE && current_pool->onMessage == nullptr) { + worker->queue = current_pool->queue; + worker->msgqueue_mode = SW_MSGQUEUE_BALANCE; + } php_swoole_process_set_worker(zprocess, worker); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - zend::Process *proc = new zend::Process(zend::PIPE_TYPE_STREAM, pp->enable_coroutine); + zend::Process *proc = new zend::Process(zend::PIPE_TYPE_STREAM, current_pool->async); worker->ptr2 = proc; (void) add_index_zval(zworkers, worker_id, zprocess); } else { @@ -635,14 +729,20 @@ static PHP_METHOD(swoole_process_pool, getProcess) { static PHP_METHOD(swoole_process_pool, stop) { if (current_pool) { current_pool->running = false; + if (current_worker) { + current_pool->stop(current_worker); + } } } static PHP_METHOD(swoole_process_pool, shutdown) { - zval *retval = - sw_zend_read_property_ex(swoole_process_pool_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_PID), 0); - long pid = zval_get_long(retval); - RETURN_BOOL(swoole_kill(pid, SIGTERM) == 0); + long pid = zend::object_get_long(ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_PID)); + if (pid > 0) { + RETURN_BOOL(swoole_kill(pid, SIGTERM) == 0); + } else { + zend_throw_exception(swoole_exception_ce, "invalid master pid", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } } static PHP_METHOD(swoole_process_pool, __destruct) {} diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc deleted file mode 100644 index f86e8be2875..00000000000 --- a/ext-src/swoole_redis_coro.cc +++ /dev/null @@ -1,5506 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#include "php_swoole_cxx.h" - -#include "thirdparty/hiredis/hiredis.h" - -#include "ext/standard/php_var.h" - -using swoole::coroutine::Socket; -using namespace swoole; - -#define SW_REDIS_COMMAND_ALLOC_ARGS_ARR zval *z_args = (zval *) emalloc(argc * sizeof(zval)); -#define SW_REDIS_COMMAND_ARGS_TYPE(arg) Z_TYPE(arg) -#define SW_REDIS_COMMAND_ARGS_LVAL(arg) Z_LVAL(arg) -#define SW_REDIS_COMMAND_ARGS_DVAL(arg) Z_DVAL(arg) -#define SW_REDIS_COMMAND_ARGS_ARRVAL(arg) Z_ARRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRVAL(arg) Z_STRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRLEN(arg) Z_STRLEN(arg) -#define SW_REDIS_COMMAND_ARGS_REF(arg) &arg - -#define SW_REDIS_COMMAND_BUFFER_SIZE 64 -#define SW_BITOP_MIN_OFFSET 0 -#define SW_BITOP_MAX_OFFSET 4294967295 -#define SW_REDIS_TYPE_NOT_FOUND 0 -#define SW_REDIS_TYPE_STRING 1 -#define SW_REDIS_TYPE_SET 2 -#define SW_REDIS_TYPE_LIST 3 -#define SW_REDIS_TYPE_ZSET 4 -#define SW_REDIS_TYPE_HASH 5 - -/* The same errCode define with hiredis */ -enum swRedisError { - SW_REDIS_ERR_IO = 1, /* Error in read or write */ - SW_REDIS_ERR_OTHER = 2, /* Everything else... */ - SW_REDIS_ERR_EOF = 3, /* End of file */ - SW_REDIS_ERR_PROTOCOL = 4, /* Protocol error */ - SW_REDIS_ERR_OOM = 5, /* Out of memory */ - SW_REDIS_ERR_CLOSED = 6, /* Closed */ - SW_REDIS_ERR_NOAUTH = 7, /* Authentication required */ - SW_REDIS_ERR_ALLOC = 8, /* Alloc failed */ -}; - -/* Extended SET argument detection */ -// clang-format off -#define IS_EX_ARG(a) \ - ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_PX_ARG(a) \ - ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_NX_ARG(a) \ - ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_XX_ARG(a) \ - ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') - -static zend_class_entry *swoole_redis_coro_ce; -static zend_object_handlers swoole_redis_coro_handlers; - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, config) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, serialize) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setOptions, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setDefer, 0, 0, 1) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_value, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_long, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, integer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_request, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, params, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_append, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_auth, 0, 0, 1) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgrewriteaof, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitop, 0, 0, 3) - ZEND_ARG_INFO(0, operation) - ZEND_ARG_INFO(0, ret_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_blPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brpoplpush, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_close, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dbSize, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_debug, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dump, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_eval, 0, 0, 1) - ZEND_ARG_INFO(0, script) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_evalsha, 0, 0, 1) - ZEND_ARG_INFO(0, script_sha) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exec, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exists, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_expireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushAll, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushDB, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getBit, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getKeys, 0, 0, 1) - ZEND_ARG_INFO(0, pattern) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getSet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hExists, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGetAll, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrByFloat, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hKeys, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMget, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMset, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSetNx, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hVals, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrByFloat, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGetRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lInsert, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, position) - ZEND_ARG_INFO(0, pivot) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lRemove, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lastSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_listTrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_move, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mset, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_msetnx, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_multi, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_persist, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpire, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfadd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, elements) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfmerge, 0, 0, 2) - ZEND_ARG_INFO(0, dstkey) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ping, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psetex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_punsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_publish, 0, 0, 2) - ZEND_ARG_INFO(0, channel) - ZEND_ARG_INFO(0, message) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_randomKey, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameKey, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameNx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_restore, 0, 0, 3) - ZEND_ARG_INFO(0, ttl) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_role, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rpoplpush, 0, 0, 2) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sAdd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sContains, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiff, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiffStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInter, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInterStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMembers, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMove, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRandMember, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnion, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnionStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_save, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_script, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) - ZEND_ARG_INFO(0, args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_select, 0, 0, 1) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, opt) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setBit, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setTimeout, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setnx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_strlen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_subscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_time, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_type, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unwatch, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_watch, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, score) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCount, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDelete, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zInter, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zScore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zUnion, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_del, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrem, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ltrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mget, 0, 0, 1) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rename, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_scard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRem, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zinterstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zunionstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, pairs) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRead, 0, 0, 1) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xTrim, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreate, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, mkstream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupSetId, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDestroy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreateConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDelConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xReadGroup, 0, 0, 3) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xPending, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAck, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAutoClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoConsumers, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoGroups, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoStream, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() -// clang-format on - -#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a)) -#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a)) - -struct RedisClient { - redisContext *context; - struct { - bool auth; - long db_num; - bool subscribe; - } session; - double connect_timeout; - double timeout; - bool serialize; - bool defer; - uint8_t reconnect_interval; - uint8_t reconnected_count; - bool auth; - bool compatibility_mode; - long database; - zval *zobject; - zval _zobject; - zend_object std; -}; - -#define SW_REDIS_COMMAND_CHECK \ - Coroutine::get_current_safe(); \ - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - -#define SW_REDIS_COMMAND_ARGV_FILL(str, str_len) \ - argvlen[i] = str_len; \ - argv[i] = estrndup(str, str_len); \ - i++; - -#define SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(_val) \ - if (redis->serialize) { \ - smart_str sstr = {}; \ - php_serialize_data_t s_ht; \ - PHP_VAR_SERIALIZE_INIT(s_ht); \ - php_var_serialize(&sstr, _val, &s_ht); \ - argvlen[i] = (size_t) sstr.s->len; \ - argv[i] = estrndup(sstr.s->val, sstr.s->len); \ - zend_string_release(sstr.s); \ - PHP_VAR_SERIALIZE_DESTROY(s_ht); \ - } else { \ - zend_string *convert_str = zval_get_string(_val); \ - argvlen[i] = ZSTR_LEN(convert_str); \ - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); \ - zend_string_release(convert_str); \ - } \ - i++; - -#define SW_REDIS_COMMAND_ALLOC_ARGV \ - size_t stack_argvlen[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - char *stack_argv[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - size_t *argvlen; \ - char **argv; \ - if (argc > SW_REDIS_COMMAND_BUFFER_SIZE) { \ - argvlen = (size_t *) emalloc(sizeof(size_t) * (argc)); \ - argv = (char **) emalloc(sizeof(char *) * (argc)); \ - } else { \ - argvlen = stack_argvlen; \ - argv = stack_argv; \ - } - -#define SW_REDIS_COMMAND_INCREASE_ARGV(_new_argc) \ - if (_new_argc > SW_REDIS_COMMAND_BUFFER_SIZE && _new_argc > argc) { \ - size_t *tmp_argvlen; \ - char **tmp_argv; \ - tmp_argvlen = (size_t *) emalloc(sizeof(size_t) * (_new_argc)); \ - tmp_argv = (char **) emalloc(sizeof(char *) * (_new_argc)); \ - for (int argc_i = 0; argc_i < argc; argc_i++) { \ - tmp_argvlen[argc_i] = argvlen[argc_i]; \ - tmp_argv[argc_i] = argv[argc_i]; \ - } \ - argvlen = tmp_argvlen; \ - argv = tmp_argv; \ - } \ - argc = _new_argc; - -#define SW_REDIS_COMMAND_FREE_ARGV \ - if (argv != stack_argv) { \ - efree(argvlen); \ - efree(argv); \ - } - -enum { SW_REDIS_MODE_MULTI, SW_REDIS_MODE_PIPELINE }; - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply); - -static sw_inline RedisClient *php_swoole_redis_coro_fetch_object(zend_object *obj) { - return (RedisClient *) ((char *) obj - swoole_redis_coro_handlers.offset); -} - -static sw_inline RedisClient *php_swoole_get_redis_client(zval *zobject) { - RedisClient *redis = (RedisClient *) php_swoole_redis_coro_fetch_object(Z_OBJ_P(zobject)); - if (UNEXPECTED(!redis)) { - php_swoole_fatal_error(E_ERROR, "you must call Redis constructor first"); - } - return redis; -} - -static sw_inline Socket *swoole_redis_coro_get_socket(redisContext *context) { - if (context->fd > 0 && SwooleTG.reactor) { - return swoole_coroutine_get_socket_object(context->fd); - } - return nullptr; -} - -static sw_inline bool swoole_redis_coro_close(RedisClient *redis) { - if (redis->context) { - int sockfd = redis->context->fd; - Socket *socket = swoole_redis_coro_get_socket(redis->context); - swoole_trace_log(SW_TRACE_REDIS_CLIENT, "redis connection closed, fd=%d", sockfd); - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("connected"), 0); - if (!(socket && socket->has_bound())) { - redisFreeKeepFd(redis->context); - redis->context = nullptr; - redis->session = {false, 0, false}; - } - if (socket) { - swoole_coroutine_close(sockfd); - } - return true; - } - return false; -} - -static void php_swoole_redis_coro_free_object(zend_object *object) { - RedisClient *redis = php_swoole_redis_coro_fetch_object(object); - - if (redis && redis->context) { - swoole_redis_coro_close(redis); - } - - zend_object_std_dtor(&redis->std); -} - -static zend_object *php_swoole_redis_coro_create_object(zend_class_entry *ce) { - RedisClient *redis = (RedisClient *) zend_object_alloc(sizeof(RedisClient), ce); - zend_object_std_init(&redis->std, ce); - object_properties_init(&redis->std, ce); - redis->std.handlers = &swoole_redis_coro_handlers; - return &redis->std; -} - -static sw_inline int sw_redis_convert_err(int err) { - switch (err) { - case SW_REDIS_ERR_IO: - return errno; - case SW_REDIS_ERR_EOF: - case SW_REDIS_ERR_CLOSED: - return ECONNRESET; - case SW_REDIS_ERR_OTHER: - return EINVAL; - case SW_REDIS_ERR_OOM: - case SW_REDIS_ERR_ALLOC: - return ENOMEM; - case SW_REDIS_ERR_PROTOCOL: - return EPROTO; - case SW_REDIS_ERR_NOAUTH: - return EACCES; - case 0: - return 0; - default: - return errno; - } -} - -static sw_inline void swoole_redis_handle_assoc_array_result(zval *return_value, bool str2double) { - zval *zkey, *zvalue; - zval zret; - bool is_key = false; - - array_init(&zret); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(return_value), zvalue) { - if ((is_key = !is_key)) { - zkey = zvalue; - } else { - if (str2double) { - convert_to_double(zvalue); - } else { - Z_ADDREF_P(zvalue); - } - add_assoc_zval_ex(&zret, Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len); -static bool redis_select_db(RedisClient *redis, long db_number); -static void redis_request( - RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry = false); - -static bool swoole_redis_coro_connect(RedisClient *redis) { - zval *zobject = redis->zobject; - redisContext *context; - Socket *socket; - struct timeval tv; - zval *ztmp; - zval *zhost = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), 0); - zval *zport = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_PORT), 0); - zend::String host(zhost); - zend_long port = zval_get_long(zport); - - if (host.len() == 0) { - php_swoole_fatal_error(E_WARNING, "The host is empty"); - return false; - } - - if (redis->context) { - context = redis->context; - if (context->connection_type == REDIS_CONN_TCP && strcmp(context->tcp.host, host.val()) == 0 && - context->tcp.port == port) { - return true; - } else if (context->connection_type == REDIS_CONN_UNIX && - (strstr(host.val(), context->unix_sock.path) - host.val()) + strlen(context->unix_sock.path) == - host.len()) { - return true; - } else { - swoole_redis_coro_close(redis); - } - } - - php_swoole_check_reactor(); - - if (redis->connect_timeout > 0) { - tv.tv_sec = redis->connect_timeout; - tv.tv_usec = (redis->connect_timeout - (double) tv.tv_sec) * 1000 * 1000; - } - if (SW_STRCASECT(host.val(), host.len(), "unix:/")) { - context = redisConnectUnixWithTimeout(host.val() + 5 + strspn(host.val() + 5, "/") - 1, tv); - } else { - if (port <= 0 || port > SW_CLIENT_MAX_PORT) { - php_swoole_fatal_error(E_WARNING, "The port " ZEND_LONG_FMT " is invalid", port); - return false; - } - context = redisConnectWithTimeout(host.val(), (int) port, tv); - } - - redis->context = context; - - if (!context) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_ALLOC); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_ALLOC)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "cannot allocate redis context"); - return false; - } - if (context->err) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), context->err); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(context->err)); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), context->errstr); - swoole_redis_coro_close(redis); - return false; - } - if (!(socket = swoole_redis_coro_get_socket(context))) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "Can not found the connection"); - swoole_redis_coro_close(redis); - return false; - } - - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - redis->reconnected_count = 0; - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), context->fd); - - // auth and select db after connected - zval *zsetting = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zsetting); - - if (php_swoole_array_get_value(vht, "password", ztmp)) { - zend::String passowrd(ztmp); - if (passowrd.len() > 0 && !redis_auth(redis, passowrd.val(), passowrd.len())) { - swoole_redis_coro_close(redis); - return false; - } - } - if (php_swoole_array_get_value(vht, "database", ztmp)) { - zend_long db_number = zval_get_long(ztmp); - // default is 0, don't need select - if (db_number > 0 && !redis_select_db(redis, db_number)) { - swoole_redis_coro_close(redis); - return false; - } - } - return true; -} - -static sw_inline bool swoole_redis_coro_keep_liveness(RedisClient *redis) { - Socket *socket = nullptr; - if (!redis->context || !(socket = swoole_redis_coro_get_socket(redis->context)) || !socket->check_liveness()) { - if (socket) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), socket->errMsg); - } - swoole_redis_coro_close(redis); - for (; redis->reconnected_count < redis->reconnect_interval; redis->reconnected_count++) { - if (swoole_redis_coro_connect(redis)) { - return true; - } - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - // Notice: do not update errCode - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "connection is not available"); - return false; - } - return true; -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("AUTH", 4) - SW_REDIS_COMMAND_ARGV_FILL(pw, pw_len) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.auth = true; - } - return ret; -} - -static bool redis_select_db(RedisClient *redis, long db_number) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - char str[32]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("SELECT", 6) - sprintf(str, "%ld", db_number); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.db_num = db_number; - } - return ret; -} - -static void redis_request( - RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry) { - redisReply *reply = nullptr; - if (!swoole_redis_coro_keep_liveness(redis)) { - ZVAL_FALSE(return_value); - } else { - // must clear err before request - redis->context->err = 0; - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), ""); - if (redis->defer) { - if (redisAppendCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen) == - REDIS_ERR) { - goto _error; - } else { - ZVAL_TRUE(return_value); - } - } else { - reply = - (redisReply *) redisCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen); - if (reply == nullptr) { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - ZVAL_FALSE(return_value); - swoole_redis_coro_close(redis); - } else { - // Redis Cluster - if (reply->type == REDIS_REPLY_ERROR && - (!strncmp(reply->str, "MOVED", 5) || !strcmp(reply->str, "ASK"))) { - char *p1, *p2; - // MOVED 1234 127.0.0.1:1234 - p1 = strrchr(reply->str, ' ') + 1; // MOVED 1234 [p1]27.0.0.1:1234 - p2 = strrchr(p1, ':'); // MOVED 1234 [p1]27.0.0.1[p2]1234 - *p2 = '\0'; - int port = atoi(p2 + 1); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("host"), p1); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("port"), port); - - if (swoole_redis_coro_connect(redis) > 0) { - freeReplyObject(reply); - redis_request(redis, argc, argv, argvlen, return_value, retry); - return; - } else { - ZVAL_FALSE(return_value); - } - } - // Normal Response - else { - swoole_redis_coro_parse_result(redis, return_value, reply); - } - freeReplyObject(reply); - } - } - } - SW_LOOP_N(argc) { - efree(argv[i]); - } -} - -static sw_inline void sw_redis_command_empty(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[1]; - char *argv[1]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - redis_request(redis, 1, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_var_key( - INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len, int min_argc, int has_timeout) { - long timeout; - int argc = ZEND_NUM_ARGS(); - if (argc < min_argc) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc == 0 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - zend_bool single_array = 0; - if (has_timeout == 0) { - single_array = argc == 1 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY; - } else { - single_array = argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY && - SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) == IS_LONG; - timeout = SW_REDIS_COMMAND_ARGS_LVAL(z_args[1]); - } - if (single_array) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 1; - } else { - argc++; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - char buf[32]; - size_t buf_len; - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } else { - if (has_timeout && SW_REDIS_COMMAND_ARGS_TYPE(z_args[argc - 2]) != IS_LONG) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "Timeout value must be a LONG"); - efree(z_args); - RETURN_FALSE; - } - int j, tail; - tail = has_timeout ? argc - 2 : argc - 1; - for (j = 0; j < tail; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, SW_REDIS_COMMAND_ARGS_LVAL(z_args[tail])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static inline void sw_redis_command_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[2]; - char *argv[2]; - int argc = 2; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode) { - if (ZVAL_IS_ARRAY(return_value) && sw_mem_equal(ZEND_STRL("HGETALL"), cmd, cmd_len)) { - swoole_redis_handle_assoc_array_result(return_value, false); - } else if (ZVAL_IS_NULL(return_value) && sw_mem_equal(ZEND_STRL("GET"), cmd, cmd_len)) { - RETURN_FALSE; - } - } -} - -static sw_inline void sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - int argc = ZEND_NUM_ARGS(); - // We at least need a key and one value - if (argc < 2) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - // Make sure we at least have a key, and we can get other args - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - for (j = 1; j < argc - 1; ++j) { - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j])) - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &l_val, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &l_val, &val, &val_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &key_len, &l_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l1_val, l2_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &l1_val, &l2_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l1_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%ld", l2_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - double d_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &d_val) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%f", d_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key1, *key2; - size_t key1_len, key2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key1, &key1_len, &key2, &key2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, 3, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_NULL(return_value) && strncmp("ZRANK", cmd, cmd_len) == 0) { - RETURN_FALSE; - } -} - -static sw_inline void sw_redis_command_key_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, 3, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &val1, &val1_len, &val2, &val2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_xrange(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &key_len, &val1, &val1_len, &val2, &val2_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() == 4 ? 6 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - if (count > 0) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_redis_coro, __construct); -static PHP_METHOD(swoole_redis_coro, __destruct); -static PHP_METHOD(swoole_redis_coro, connect); -static PHP_METHOD(swoole_redis_coro, getAuth); -static PHP_METHOD(swoole_redis_coro, getDBNum); -static PHP_METHOD(swoole_redis_coro, getOptions); -static PHP_METHOD(swoole_redis_coro, setOptions); -static PHP_METHOD(swoole_redis_coro, getDefer); -static PHP_METHOD(swoole_redis_coro, setDefer); -static PHP_METHOD(swoole_redis_coro, recv); -static PHP_METHOD(swoole_redis_coro, request); -static PHP_METHOD(swoole_redis_coro, close); -/*---------------------Redis Command------------------------*/ -static PHP_METHOD(swoole_redis_coro, set); -static PHP_METHOD(swoole_redis_coro, setBit); -static PHP_METHOD(swoole_redis_coro, setEx); -static PHP_METHOD(swoole_redis_coro, psetEx); -static PHP_METHOD(swoole_redis_coro, lSet); -static PHP_METHOD(swoole_redis_coro, get); -static PHP_METHOD(swoole_redis_coro, mGet); -static PHP_METHOD(swoole_redis_coro, del); -static PHP_METHOD(swoole_redis_coro, hDel); -static PHP_METHOD(swoole_redis_coro, hSet); -static PHP_METHOD(swoole_redis_coro, hMSet); -static PHP_METHOD(swoole_redis_coro, hSetNx); -static PHP_METHOD(swoole_redis_coro, mSet); -static PHP_METHOD(swoole_redis_coro, mSetNx); -static PHP_METHOD(swoole_redis_coro, getKeys); -static PHP_METHOD(swoole_redis_coro, exists); -static PHP_METHOD(swoole_redis_coro, type); -static PHP_METHOD(swoole_redis_coro, strLen); -static PHP_METHOD(swoole_redis_coro, lPop); -static PHP_METHOD(swoole_redis_coro, blPop); -static PHP_METHOD(swoole_redis_coro, rPop); -static PHP_METHOD(swoole_redis_coro, brPop); -static PHP_METHOD(swoole_redis_coro, bRPopLPush); -static PHP_METHOD(swoole_redis_coro, lSize); -static PHP_METHOD(swoole_redis_coro, sSize); -static PHP_METHOD(swoole_redis_coro, sPop); -static PHP_METHOD(swoole_redis_coro, sMembers); -static PHP_METHOD(swoole_redis_coro, sRandMember); -static PHP_METHOD(swoole_redis_coro, persist); -static PHP_METHOD(swoole_redis_coro, ttl); -static PHP_METHOD(swoole_redis_coro, pttl); -static PHP_METHOD(swoole_redis_coro, zCard); -static PHP_METHOD(swoole_redis_coro, hLen); -static PHP_METHOD(swoole_redis_coro, hKeys); -static PHP_METHOD(swoole_redis_coro, hVals); -static PHP_METHOD(swoole_redis_coro, hGetAll); -static PHP_METHOD(swoole_redis_coro, restore); -static PHP_METHOD(swoole_redis_coro, dump); -static PHP_METHOD(swoole_redis_coro, debug); -static PHP_METHOD(swoole_redis_coro, renameKey); -static PHP_METHOD(swoole_redis_coro, renameNx); -static PHP_METHOD(swoole_redis_coro, rpoplpush); -static PHP_METHOD(swoole_redis_coro, randomKey); -static PHP_METHOD(swoole_redis_coro, pfadd); -static PHP_METHOD(swoole_redis_coro, pfcount); -static PHP_METHOD(swoole_redis_coro, pfmerge); -static PHP_METHOD(swoole_redis_coro, ping); -static PHP_METHOD(swoole_redis_coro, auth); -static PHP_METHOD(swoole_redis_coro, unwatch); -static PHP_METHOD(swoole_redis_coro, watch); -static PHP_METHOD(swoole_redis_coro, save); -static PHP_METHOD(swoole_redis_coro, bgSave); -static PHP_METHOD(swoole_redis_coro, lastSave); -static PHP_METHOD(swoole_redis_coro, flushDB); -static PHP_METHOD(swoole_redis_coro, flushAll); -static PHP_METHOD(swoole_redis_coro, dbSize); -static PHP_METHOD(swoole_redis_coro, bgrewriteaof); -static PHP_METHOD(swoole_redis_coro, time); -static PHP_METHOD(swoole_redis_coro, role); -static PHP_METHOD(swoole_redis_coro, setRange); -static PHP_METHOD(swoole_redis_coro, setNx); -static PHP_METHOD(swoole_redis_coro, getSet); -static PHP_METHOD(swoole_redis_coro, append); -static PHP_METHOD(swoole_redis_coro, lPushx); -static PHP_METHOD(swoole_redis_coro, lPush); -static PHP_METHOD(swoole_redis_coro, rPush); -static PHP_METHOD(swoole_redis_coro, rPushx); -static PHP_METHOD(swoole_redis_coro, sContains); -static PHP_METHOD(swoole_redis_coro, zScore); -static PHP_METHOD(swoole_redis_coro, zRank); -static PHP_METHOD(swoole_redis_coro, zRevRank); -static PHP_METHOD(swoole_redis_coro, hGet); -static PHP_METHOD(swoole_redis_coro, hMGet); -static PHP_METHOD(swoole_redis_coro, hExists); -static PHP_METHOD(swoole_redis_coro, publish); -static PHP_METHOD(swoole_redis_coro, zIncrBy); -static PHP_METHOD(swoole_redis_coro, zAdd); -static PHP_METHOD(swoole_redis_coro, zPopMin); -static PHP_METHOD(swoole_redis_coro, zPopMax); -static PHP_METHOD(swoole_redis_coro, bzPopMin); -static PHP_METHOD(swoole_redis_coro, bzPopMax); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore); -static PHP_METHOD(swoole_redis_coro, zCount); -static PHP_METHOD(swoole_redis_coro, zRange); -static PHP_METHOD(swoole_redis_coro, zRevRange); -static PHP_METHOD(swoole_redis_coro, zRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRangeByLex); -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex); -static PHP_METHOD(swoole_redis_coro, zInter); -static PHP_METHOD(swoole_redis_coro, zUnion); -static PHP_METHOD(swoole_redis_coro, incrBy); -static PHP_METHOD(swoole_redis_coro, hIncrBy); -static PHP_METHOD(swoole_redis_coro, incr); -static PHP_METHOD(swoole_redis_coro, decrBy); -static PHP_METHOD(swoole_redis_coro, decr); -static PHP_METHOD(swoole_redis_coro, getBit); -static PHP_METHOD(swoole_redis_coro, lGet); -static PHP_METHOD(swoole_redis_coro, lInsert); -static PHP_METHOD(swoole_redis_coro, setTimeout); -static PHP_METHOD(swoole_redis_coro, pexpire); -static PHP_METHOD(swoole_redis_coro, expireAt); -static PHP_METHOD(swoole_redis_coro, pexpireAt); -static PHP_METHOD(swoole_redis_coro, move); -static PHP_METHOD(swoole_redis_coro, select); -static PHP_METHOD(swoole_redis_coro, getRange); -static PHP_METHOD(swoole_redis_coro, listTrim); -static PHP_METHOD(swoole_redis_coro, lGetRange); -static PHP_METHOD(swoole_redis_coro, lRem); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank); -static PHP_METHOD(swoole_redis_coro, incrByFloat); -static PHP_METHOD(swoole_redis_coro, hIncrByFloat); -static PHP_METHOD(swoole_redis_coro, bitCount); -static PHP_METHOD(swoole_redis_coro, bitOp); -static PHP_METHOD(swoole_redis_coro, sAdd); -static PHP_METHOD(swoole_redis_coro, sMove); -static PHP_METHOD(swoole_redis_coro, sDiff); -static PHP_METHOD(swoole_redis_coro, sDiffStore); -static PHP_METHOD(swoole_redis_coro, sUnion); -static PHP_METHOD(swoole_redis_coro, sUnionStore); -static PHP_METHOD(swoole_redis_coro, sInter); -static PHP_METHOD(swoole_redis_coro, sInterStore); -static PHP_METHOD(swoole_redis_coro, sRemove); -static PHP_METHOD(swoole_redis_coro, zDelete); -static PHP_METHOD(swoole_redis_coro, subscribe); -static PHP_METHOD(swoole_redis_coro, pSubscribe); -static PHP_METHOD(swoole_redis_coro, unsubscribe); -static PHP_METHOD(swoole_redis_coro, pUnSubscribe); -static PHP_METHOD(swoole_redis_coro, multi); -static PHP_METHOD(swoole_redis_coro, exec); -static PHP_METHOD(swoole_redis_coro, eval); -static PHP_METHOD(swoole_redis_coro, evalSha); -static PHP_METHOD(swoole_redis_coro, script); -static PHP_METHOD(swoole_redis_coro, xLen); -static PHP_METHOD(swoole_redis_coro, xAdd); -static PHP_METHOD(swoole_redis_coro, xRead); -static PHP_METHOD(swoole_redis_coro, xDel); -static PHP_METHOD(swoole_redis_coro, xRange); -static PHP_METHOD(swoole_redis_coro, xRevRange); -static PHP_METHOD(swoole_redis_coro, xTrim); -static PHP_METHOD(swoole_redis_coro, xGroupCreate); -static PHP_METHOD(swoole_redis_coro, xGroupSetId); -static PHP_METHOD(swoole_redis_coro, xGroupDestroy); -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer); -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer); -static PHP_METHOD(swoole_redis_coro, xReadGroup); -static PHP_METHOD(swoole_redis_coro, xPending); -static PHP_METHOD(swoole_redis_coro, xAck); -static PHP_METHOD(swoole_redis_coro, xClaim); -static PHP_METHOD(swoole_redis_coro, xAutoClaim); -static PHP_METHOD(swoole_redis_coro, xInfoConsumers); -static PHP_METHOD(swoole_redis_coro, xInfoGroups); -static PHP_METHOD(swoole_redis_coro, xInfoStream); -SW_EXTERN_C_END -/*---------------------Redis Command End------------------------*/ -// clang-format off -static const zend_function_entry swoole_redis_coro_methods[] = -{ - PHP_ME(swoole_redis_coro, __construct, arginfo_swoole_redis_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, __destruct, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, connect, arginfo_swoole_redis_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getAuth, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDBNum, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getOptions, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setOptions, arginfo_swoole_redis_coro_setOptions, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDefer, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setDefer, arginfo_swoole_redis_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, recv, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, request, arginfo_swoole_redis_coro_request, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, close, arginfo_swoole_redis_coro_close, ZEND_ACC_PUBLIC) - /*---------------------Redis Command------------------------*/ - PHP_ME(swoole_redis_coro, set, arginfo_swoole_redis_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setBit, arginfo_swoole_redis_coro_setBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setEx, arginfo_swoole_redis_coro_setex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, psetEx, arginfo_swoole_redis_coro_psetex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSet, arginfo_swoole_redis_coro_lSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, get, arginfo_swoole_redis_coro_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mGet, arginfo_swoole_redis_coro_mget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hDel, arginfo_swoole_redis_coro_hDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSet, arginfo_swoole_redis_coro_hSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMSet, arginfo_swoole_redis_coro_hMset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSetNx, arginfo_swoole_redis_coro_hSetNx, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, delete, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSet, arginfo_swoole_redis_coro_mset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSetNx, arginfo_swoole_redis_coro_msetnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, keys, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exists, arginfo_swoole_redis_coro_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, type, arginfo_swoole_redis_coro_type, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, strLen, arginfo_swoole_redis_coro_strlen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPop, arginfo_swoole_redis_coro_lPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, blPop, arginfo_swoole_redis_coro_blPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPop, arginfo_swoole_redis_coro_rPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, brPop, arginfo_swoole_redis_coro_brPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bRPopLPush, arginfo_swoole_redis_coro_brpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSize, arginfo_swoole_redis_coro_lSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lLen, lSize, arginfo_swoole_redis_coro_lLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sSize, arginfo_swoole_redis_coro_sSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, scard, sSize, arginfo_swoole_redis_coro_scard, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sPop, arginfo_swoole_redis_coro_sPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMembers, arginfo_swoole_redis_coro_sMembers, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sGetMembers, sMembers, arginfo_swoole_redis_coro_key, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRandMember, arginfo_swoole_redis_coro_sRandMember, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, persist, arginfo_swoole_redis_coro_persist, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ttl, arginfo_swoole_redis_coro_ttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pttl, arginfo_swoole_redis_coro_pttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCard, arginfo_swoole_redis_coro_zCard, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zSize, zCard, arginfo_swoole_redis_coro_zSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hLen, arginfo_swoole_redis_coro_hLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hKeys, arginfo_swoole_redis_coro_hKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hVals, arginfo_swoole_redis_coro_hVals, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGetAll, arginfo_swoole_redis_coro_hGetAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, debug, arginfo_swoole_redis_coro_debug, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, restore, arginfo_swoole_redis_coro_restore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dump, arginfo_swoole_redis_coro_dump, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameKey, arginfo_swoole_redis_coro_renameKey, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, rename, renameKey, arginfo_swoole_redis_coro_rename, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameNx, arginfo_swoole_redis_coro_renameNx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rpoplpush, arginfo_swoole_redis_coro_rpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, randomKey, arginfo_swoole_redis_coro_randomKey, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfadd, arginfo_swoole_redis_coro_pfadd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfcount, arginfo_swoole_redis_coro_pfcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfmerge, arginfo_swoole_redis_coro_pfmerge, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ping, arginfo_swoole_redis_coro_ping, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, auth, arginfo_swoole_redis_coro_auth, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unwatch, arginfo_swoole_redis_coro_unwatch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, watch, arginfo_swoole_redis_coro_watch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, save, arginfo_swoole_redis_coro_save, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgSave, arginfo_swoole_redis_coro_bgSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lastSave, arginfo_swoole_redis_coro_lastSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushDB, arginfo_swoole_redis_coro_flushDB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushAll, arginfo_swoole_redis_coro_flushAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dbSize, arginfo_swoole_redis_coro_dbSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgrewriteaof, arginfo_swoole_redis_coro_bgrewriteaof, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, time, arginfo_swoole_redis_coro_time, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, role, arginfo_swoole_redis_coro_role, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setRange, arginfo_swoole_redis_coro_setRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setNx, arginfo_swoole_redis_coro_setnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getSet, arginfo_swoole_redis_coro_getSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, append, arginfo_swoole_redis_coro_append, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPushx, arginfo_swoole_redis_coro_lPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPush, arginfo_swoole_redis_coro_lPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPush, arginfo_swoole_redis_coro_rPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPushx, arginfo_swoole_redis_coro_rPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sContains, arginfo_swoole_redis_coro_sContains, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sismember, sContains, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zScore, arginfo_swoole_redis_coro_zScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRank, arginfo_swoole_redis_coro_zRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRank, arginfo_swoole_redis_coro_zRevRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGet, arginfo_swoole_redis_coro_hGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMGet, arginfo_swoole_redis_coro_hMget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hExists, arginfo_swoole_redis_coro_hExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, publish, arginfo_swoole_redis_coro_publish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zIncrBy, arginfo_swoole_redis_coro_zIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zAdd, arginfo_swoole_redis_coro_zAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMin, arginfo_swoole_redis_coro_zPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMax, arginfo_swoole_redis_coro_zPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMin, arginfo_swoole_redis_coro_bzPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMax, arginfo_swoole_redis_coro_bzPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByScore, arginfo_swoole_redis_coro_zDeleteRangeByScore, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByScore, zDeleteRangeByScore, arginfo_swoole_redis_coro_zRemRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCount, arginfo_swoole_redis_coro_zCount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRange, arginfo_swoole_redis_coro_zRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRange, arginfo_swoole_redis_coro_zRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByScore, arginfo_swoole_redis_coro_zRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByScore, arginfo_swoole_redis_coro_zRevRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByLex, arginfo_swoole_redis_coro_zRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByLex, arginfo_swoole_redis_coro_zRevRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zInter, arginfo_swoole_redis_coro_zInter, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zinterstore, zInter, arginfo_swoole_redis_coro_zinterstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zUnion, arginfo_swoole_redis_coro_zUnion, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zunionstore, zUnion, arginfo_swoole_redis_coro_zunionstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrBy, arginfo_swoole_redis_coro_incrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrBy, arginfo_swoole_redis_coro_hIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incr, arginfo_swoole_redis_coro_incr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decrBy, arginfo_swoole_redis_coro_decrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decr, arginfo_swoole_redis_coro_decr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getBit, arginfo_swoole_redis_coro_getBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lInsert, arginfo_swoole_redis_coro_lInsert, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGet, arginfo_swoole_redis_coro_lGet, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lIndex, lGet, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setTimeout, arginfo_swoole_redis_coro_setTimeout, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, expire, setTimeout, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpire, arginfo_swoole_redis_coro_pexpire, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, expireAt, arginfo_swoole_redis_coro_expireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpireAt, arginfo_swoole_redis_coro_pexpireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, move, arginfo_swoole_redis_coro_move, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, select, arginfo_swoole_redis_coro_select, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getRange, arginfo_swoole_redis_coro_getRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, listTrim, arginfo_swoole_redis_coro_listTrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, ltrim, listTrim, arginfo_swoole_redis_coro_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGetRange, arginfo_swoole_redis_coro_lGetRange, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRange, lGetRange, arginfo_swoole_redis_coro_lrange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lRem, arginfo_swoole_redis_coro_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRemove,lRem, arginfo_swoole_redis_coro_lRemove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByRank, arginfo_swoole_redis_coro_zDeleteRangeByRank, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByRank, zDeleteRangeByRank, arginfo_swoole_redis_coro_zRemRangeByRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrByFloat, arginfo_swoole_redis_coro_incrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrByFloat, arginfo_swoole_redis_coro_hIncrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitCount, arginfo_swoole_redis_coro_bitcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitOp, arginfo_swoole_redis_coro_bitop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sAdd, arginfo_swoole_redis_coro_sAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMove, arginfo_swoole_redis_coro_sMove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiff, arginfo_swoole_redis_coro_sDiff, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiffStore, arginfo_swoole_redis_coro_sDiffStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnion, arginfo_swoole_redis_coro_sUnion, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnionStore, arginfo_swoole_redis_coro_sUnionStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInter, arginfo_swoole_redis_coro_sInter, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInterStore, arginfo_swoole_redis_coro_sInterStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRemove, arginfo_swoole_redis_coro_sRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, srem, sRemove, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDelete, arginfo_swoole_redis_coro_zDelete, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemove, zDelete, arginfo_swoole_redis_coro_zRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRem, zDelete, arginfo_swoole_redis_coro_zRem, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pSubscribe, arginfo_swoole_redis_coro_psubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, subscribe, arginfo_swoole_redis_coro_subscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unsubscribe, arginfo_swoole_redis_coro_unsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pUnSubscribe, arginfo_swoole_redis_coro_punsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, multi, arginfo_swoole_redis_coro_multi, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exec, arginfo_swoole_redis_coro_exec, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, eval, arginfo_swoole_redis_coro_eval, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, evalSha, arginfo_swoole_redis_coro_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, script, arginfo_swoole_redis_coro_script, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xLen, arginfo_swoole_redis_coro_xLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAdd, arginfo_swoole_redis_coro_xAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRead, arginfo_swoole_redis_coro_xRead, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xDel, arginfo_swoole_redis_coro_xDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRange, arginfo_swoole_redis_coro_xRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRevRange, arginfo_swoole_redis_coro_xRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xTrim, arginfo_swoole_redis_coro_xTrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreate, arginfo_swoole_redis_coro_xGroupCreate, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupSetId, arginfo_swoole_redis_coro_xGroupSetId, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDestroy, arginfo_swoole_redis_coro_xGroupDestroy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreateConsumer, arginfo_swoole_redis_coro_xGroupCreateConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDelConsumer, arginfo_swoole_redis_coro_xGroupDelConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xReadGroup, arginfo_swoole_redis_coro_xReadGroup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xPending, arginfo_swoole_redis_coro_xPending, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAck, arginfo_swoole_redis_coro_xAck, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xClaim, arginfo_swoole_redis_coro_xClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAutoClaim, arginfo_swoole_redis_coro_xAutoClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoConsumers, arginfo_swoole_redis_coro_xInfoConsumers, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoGroups, arginfo_swoole_redis_coro_xInfoGroups, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoStream, arginfo_swoole_redis_coro_xInfoStream, ZEND_ACC_PUBLIC) - /*---------------------Redis Command End------------------------*/ - PHP_FE_END -}; -// clang-format on - -void php_swoole_redis_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", nullptr, "Co\\Redis", swoole_redis_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_coro); - SW_SET_CLASS_CLONEABLE(swoole_redis_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_redis_coro); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_redis_coro, php_swoole_redis_coro_create_object, php_swoole_redis_coro_free_object, RedisClient, std); - - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_redis_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_redis_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errType"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_MULTI", SW_REDIS_MODE_MULTI); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_PIPELINE", SW_REDIS_MODE_PIPELINE); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_NOT_FOUND", SW_REDIS_TYPE_NOT_FOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_STRING", SW_REDIS_TYPE_STRING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_SET", SW_REDIS_TYPE_SET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_LIST", SW_REDIS_TYPE_LIST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_ZSET", SW_REDIS_TYPE_ZSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_HASH", SW_REDIS_TYPE_HASH); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_IO", SW_REDIS_ERR_IO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OTHER", SW_REDIS_ERR_OTHER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_EOF", SW_REDIS_ERR_EOF); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_PROTOCOL", SW_REDIS_ERR_PROTOCOL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OOM", SW_REDIS_ERR_OOM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_CLOSED", SW_REDIS_ERR_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_NOAUTH", SW_REDIS_ERR_NOAUTH); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_ALLOC", SW_REDIS_ERR_ALLOC); -} - -static void swoole_redis_coro_set_options(RedisClient *redis, zval *zoptions, bool backward_compatibility = false) { - zval *zsettings = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zoptions); - zval *ztmp; - - php_array_merge(Z_ARRVAL_P(zsettings), vht); - - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { - redis->connect_timeout = zval_get_double(ztmp); - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (php_swoole_array_get_value(vht, "timeout", ztmp)) { - redis->timeout = zval_get_double(ztmp); - if (backward_compatibility) { - redis->connect_timeout = redis->timeout; - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (redis->context) { - Socket *socket = swoole_redis_coro_get_socket(redis->context); - if (socket) { - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - } - } - } - if (php_swoole_array_get_value(vht, "serialize", ztmp)) { - redis->serialize = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "reconnect", ztmp)) { - redis->reconnect_interval = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); - } - if (php_swoole_array_get_value(vht, "compatibility_mode", ztmp)) { - redis->compatibility_mode = zval_is_true(ztmp); - } -} - -static PHP_METHOD(swoole_redis_coro, __construct) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - zval *zset = nullptr; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (redis->zobject) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - redis->zobject = &redis->_zobject; - redis->_zobject = *ZEND_THIS; - - redis->connect_timeout = network::Socket::default_connect_timeout; - redis->timeout = network::Socket::default_read_timeout; - redis->reconnect_interval = 1; - - // settings init - add_assoc_double(zsettings, "connect_timeout", redis->connect_timeout); - add_assoc_double(zsettings, "timeout", redis->timeout); - add_assoc_bool(zsettings, "serialize", redis->serialize); - add_assoc_long(zsettings, "reconnect", redis->reconnect_interval); - // after connected - add_assoc_string(zsettings, "password", (char *) ""); - add_assoc_long(zsettings, "database", 0); - - if (zset) { - swoole_redis_coro_set_options(redis, zset, true); - } -} - -static PHP_METHOD(swoole_redis_coro, connect) { - zval *zobject = ZEND_THIS; - char *host = nullptr; - size_t host_len = 0; - zend_long port = 0; - zend_bool serialize = 0; - - SW_REDIS_COMMAND_CHECK - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lb", &host, &host_len, &port, &serialize) == FAILURE) { - RETURN_FALSE; - } - - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("host"), host); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("port"), port); - redis->serialize = serialize; - - if (swoole_redis_coro_connect(redis) > 0) { - // clear the error code only when the developer manually tries to connect successfully - // if the kernel retries automatically, keep silent. - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, getAuth) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->session.auth) { - zval *ztmp = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (php_swoole_array_get_value(Z_ARRVAL_P(ztmp), "password", ztmp)) { - RETURN_ZVAL(ztmp, 1, 0); - } - RETURN_EMPTY_STRING(); - } - RETURN_FALSE; -} - -static PHP_METHOD(swoole_redis_coro, getDBNum) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (!redis->context) { - RETURN_FALSE; - } - RETURN_LONG(redis->session.db_num); -} - -static PHP_METHOD(swoole_redis_coro, getOptions) { - RETURN_ZVAL( - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, setOptions) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zoptions; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zoptions) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_redis_coro_set_options(redis, zoptions); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, getDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - - RETURN_BOOL(redis->defer); -} - -static PHP_METHOD(swoole_redis_coro, setDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zend_bool defer = 1; - - if (redis->session.subscribe) { - php_swoole_fatal_error(E_WARNING, "you should not use setDefer after subscribe"); - RETURN_FALSE; - } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &defer) == FAILURE) { - RETURN_FALSE; - } - redis->defer = defer; - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, recv) { - SW_REDIS_COMMAND_CHECK - - if (UNEXPECTED(!redis->context)) { - RETURN_FALSE; - } - if (UNEXPECTED(!redis->defer && !redis->session.subscribe)) { - php_swoole_fatal_error(E_WARNING, "you should not use recv without defer or subscribe"); - RETURN_FALSE; - } - - redisReply *reply; -_recv: - if (redisGetReply(redis->context, (void **) &reply) == REDIS_OK) { - swoole_redis_coro_parse_result(redis, return_value, reply); - freeReplyObject(reply); - - if (redis->session.subscribe) { - zval *ztype; - - if (!ZVAL_IS_ARRAY(return_value)) { - zval_ptr_dtor(return_value); - goto _error; - } - - ztype = zend_hash_index_find(Z_ARRVAL_P(return_value), 0); - if (Z_TYPE_P(ztype) == IS_STRING) { - char *type = Z_STRVAL_P(ztype); - - if (!strcmp(type, "unsubscribe") || !strcmp(type, "punsubscribe")) { - zval *znum = zend_hash_index_find(Z_ARRVAL_P(return_value), 2); - if (Z_LVAL_P(znum) == 0) { - redis->session.subscribe = false; - } - - return; - } else if (!strcmp(type, "message") || !strcmp(type, "pmessage") || !strcmp(type, "subscribe") || - !strcmp(type, "psubscribe")) { - return; - } - } - - zval_ptr_dtor(return_value); - goto _recv; - } - } else { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - - swoole_redis_coro_close(redis); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, close) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - RETURN_BOOL(swoole_redis_coro_close(redis)); -} - -static PHP_METHOD(swoole_redis_coro, __destruct) { - SW_PREVENT_USER_DESTRUCT(); -} - -static PHP_METHOD(swoole_redis_coro, set) { - char *key, *exp_type = nullptr, *set_type = nullptr; - size_t key_len, argc = 3; - zval *z_value, *z_opts = nullptr; - zend_long expire = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY && Z_TYPE_P(z_opts) != IS_NULL) { - RETURN_FALSE; - } - - if (z_opts && ZVAL_IS_ARRAY(z_opts)) { - HashTable *kt = Z_ARRVAL_P(z_opts); - - zend_string *zkey; - zend_ulong idx; - zval *zv; - - /* Iterate our option array */ - ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, zv) { - /* Detect PX or EX argument and validate timeout */ - if (!exp_type && zkey && IS_EX_PX_ARG(ZSTR_VAL(zkey))) { - /* Set expire type */ - exp_type = ZSTR_VAL(zkey); - - /* Try to extract timeout */ - if (Z_TYPE_P(zv) == IS_LONG) { - expire = Z_LVAL_P(zv); - } else if (Z_TYPE_P(zv) == IS_STRING) { - expire = atol(Z_STRVAL_P(zv)); - } - - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 2; - } else if (!set_type && Z_TYPE_P(zv) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(zv))) { - argc += 1; - set_type = Z_STRVAL_P(zv); - } - (void) idx; - } - ZEND_HASH_FOREACH_END(); - } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - /* Grab expiry and fail if it's < 1 */ - expire = Z_LVAL_P(z_opts); - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 1; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - - int i = 0; - if (exp_type || set_type) { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - - if (set_type) { - SW_REDIS_COMMAND_ARGV_FILL(set_type, (size_t) strlen(set_type)) - } - - if (exp_type) { - SW_REDIS_COMMAND_ARGV_FILL(exp_type, (size_t) strlen(exp_type)) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - } - } else if (expire > 0) { - SW_REDIS_COMMAND_ARGV_FILL("SETEX", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } else { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, setBit) { - char *key; - size_t key_len; - long offset; - zend_bool val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, &offset, &val) == FAILURE) { - return; - } - - // Validate our offset - if (offset < SW_BITOP_MIN_OFFSET || offset > SW_BITOP_MAX_OFFSET) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - - SW_REDIS_COMMAND_ARGV_FILL("SETBIT", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, "%ld", offset); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL(val ? "1" : "0", 1) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, setEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETEX")); -} - -static PHP_METHOD(swoole_redis_coro, psetEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PSETEX")); -} - -static PHP_METHOD(swoole_redis_coro, lSet) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LSET")); -} - -static PHP_METHOD(swoole_redis_coro, restore) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RESTORE")); -} - -static PHP_METHOD(swoole_redis_coro, dump) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DUMP")); -} - -static PHP_METHOD(swoole_redis_coro, debug) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEBUG")); -} - -static PHP_METHOD(swoole_redis_coro, get) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GET")); -} - -static PHP_METHOD(swoole_redis_coro, mGet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - zval *value; - SW_REDIS_COMMAND_ARGV_FILL("MGET", 4) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_args), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSet) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HSET", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hMSet) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 2; - zval *value; - char buf[32]; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMSET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSetNx) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - convert_to_string(z_val); - SW_REDIS_COMMAND_ARGV_FILL("HSETNX", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_val), Z_STRLEN_P(z_val)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hDel) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc < 2 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - argc++; - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HDEL", 4) - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, watch) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("WATCH"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, del) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEL"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiff) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFF"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiffStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFFSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnion) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNION"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnionStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNIONSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInter) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTER"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInterStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTERSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, mSet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSET", 4) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, mSetNx) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - return; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSETNX", 6) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, getKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("KEYS")); -} - -static PHP_METHOD(swoole_redis_coro, exists) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXISTS")); -} - -static PHP_METHOD(swoole_redis_coro, type) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TYPE")); -} - -static PHP_METHOD(swoole_redis_coro, strLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("STRLEN")); -} - -static PHP_METHOD(swoole_redis_coro, lPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPOP")); -} - -static PHP_METHOD(swoole_redis_coro, bRPopLPush) { - char *key1, *key2; - size_t key1_len, key2_len; - long timeout; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key1, &key1_len, &key2, &key2_len, &timeout) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int argc, i = 0; - argc = timeout < 0 ? 3 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - if (timeout < 0) { - SW_REDIS_COMMAND_ARGV_FILL("RPOPLPUSH", 9) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - } else { - SW_REDIS_COMMAND_ARGV_FILL("BRPOPLPUSH", 10) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - char str[32]; - sprintf(str, "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, blPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BLPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, brPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BRPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, rPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOP")); -} - -static PHP_METHOD(swoole_redis_coro, lSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LLEN")); -} - -static PHP_METHOD(swoole_redis_coro, sSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SCARD")); -} - -static PHP_METHOD(swoole_redis_coro, sPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SPOP")); -} - -static PHP_METHOD(swoole_redis_coro, sMembers) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SMEMBERS")); -} - -static PHP_METHOD(swoole_redis_coro, sRandMember) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("SRANDMEMBER", 11); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, persist) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PERSIST")); -} - -static PHP_METHOD(swoole_redis_coro, ttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TTL")); -} - -static PHP_METHOD(swoole_redis_coro, pttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PTTL")); -} - -static PHP_METHOD(swoole_redis_coro, zCard) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCARD")); -} - -static PHP_METHOD(swoole_redis_coro, hLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HLEN")); -} - -static PHP_METHOD(swoole_redis_coro, hKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HKEYS")); -} - -static PHP_METHOD(swoole_redis_coro, hVals) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HVALS")); -} - -static PHP_METHOD(swoole_redis_coro, hGetAll) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGETALL")); -} - -static PHP_METHOD(swoole_redis_coro, renameKey) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAME")); -} - -static PHP_METHOD(swoole_redis_coro, renameNx) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAMENX")); -} - -static PHP_METHOD(swoole_redis_coro, rpoplpush) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOPLPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, randomKey) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RANDOMKEY")); -} - -static PHP_METHOD(swoole_redis_coro, unwatch) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("UNWATCH")); -} - -static PHP_METHOD(swoole_redis_coro, pfadd) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFADD", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfcount) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc != 1) { - efree(z_args); - RETURN_FALSE; - } - - zend_bool single_array = 0; - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])); - single_array = 1; - } - - argc += 1; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFCOUNT", 7) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END() - } else { - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfmerge) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFMERGE", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, ping) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PING")); -} - -static PHP_METHOD(swoole_redis_coro, auth) { - char *pw; - size_t pw_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pw, &pw_len) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_stringl(zsetting, "password", pw, pw_len); - RETURN_BOOL(redis_auth(redis, pw, pw_len)); -} - -static PHP_METHOD(swoole_redis_coro, save) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SAVE")); -} - -static PHP_METHOD(swoole_redis_coro, bgSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, lastSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LASTSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, flushDB) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHDB")); -} - -static PHP_METHOD(swoole_redis_coro, flushAll) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHALL")); -} - -static PHP_METHOD(swoole_redis_coro, dbSize) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DBSIZE")); -} - -static PHP_METHOD(swoole_redis_coro, bgrewriteaof) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGREWRITEAOF")); -} - -static PHP_METHOD(swoole_redis_coro, time) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TIME")); -} - -static PHP_METHOD(swoole_redis_coro, role) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLE")); -} - -static PHP_METHOD(swoole_redis_coro, setRange) { - sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, setNx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETNX")); -} - -static PHP_METHOD(swoole_redis_coro, getSet) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETSET")); -} - -static PHP_METHOD(swoole_redis_coro, append) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("APPEND")); -} - -static PHP_METHOD(swoole_redis_coro, lPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, lPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, sContains) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SISMEMBER")); -} - -static PHP_METHOD(swoole_redis_coro, zRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGE", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zUnion) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZUNIONSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zInter) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZINTERSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - size_t argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYLEX", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - int argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYLEX", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYSCORE", 13) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYSCORE", 16) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zIncrBy) { - char *key; - size_t key_len; - double incrby; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, &incrby, &z_val) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK; - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("ZINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%f", incrby); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zAdd) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - if (argc > 0) { - convert_to_string(&z_args[0]); - } - if (argc < 3 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j, k, valid_params; - valid_params = argc - 1; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - k = 1; - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && IS_NX_XX_ARG(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]))) { - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[k])) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "CH", 2) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("CH", 2) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "INCR", 4) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("INCR", 4) - k++; - valid_params--; - } - - if (valid_params % 2 != 0) { - for (i = 0; i < 1 + k; i++) { - efree((void *) argv[i]); - } - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - for (j = k; j < argc - 1; j += 2) { - buf_len = sw_snprintf(buf, sizeof(buf), "%f", zval_get_double(&z_args[j])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j + 1])) - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMin) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMIN", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMax) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMAX", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMin) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMIN", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMax) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMAX", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zScore) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZRANK")); -} - -static PHP_METHOD(swoole_redis_coro, zRevRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREVRANK")); -} - -static PHP_METHOD(swoole_redis_coro, hGet) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGET")); -} - -static PHP_METHOD(swoole_redis_coro, hMGet) { - char *key; - zval *z_arr; - size_t argc, key_len; - HashTable *ht_chan; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - - ht_chan = Z_ARRVAL_P(z_arr); - - if ((argc = zend_hash_num_elements(ht_chan)) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - zval *value; - int i = 0; - argc = argc + 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMGET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - size_t index = 0; - zval *zkey, *zvalue; - zval zret; - array_init(&zret); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_arr), zkey) { - zend::String zkey_str(zkey); - - zvalue = zend_hash_index_find(Z_ARRVAL_P(return_value), index++); - if (ZVAL_IS_NULL(zvalue)) { - add_assoc_bool_ex(&zret, zkey_str.val(), zkey_str.len(), 0); - } else { - Z_ADDREF_P(zvalue); - add_assoc_zval_ex(&zret, zkey_str.val(), zkey_str.len(), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); - } -} - -static PHP_METHOD(swoole_redis_coro, hExists) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HEXISTS")); - - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->compatibility_mode && ZVAL_IS_LONG(return_value)) { - RETURN_BOOL(zval_get_long(return_value)); - } -} - -static PHP_METHOD(swoole_redis_coro, publish) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PUBLISH")); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zCount) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCOUNT")); -} - -static PHP_METHOD(swoole_redis_coro, incrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBY")); -} - -static PHP_METHOD(swoole_redis_coro, hIncrBy) { - char *key, *mem; - size_t key_len, mem_len; - long byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%ld", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hIncrByFloat) { - char *key, *mem; - size_t key_len, mem_len; - double byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBYFLOAT", 12) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%f", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, incr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCR")); -} - -static PHP_METHOD(swoole_redis_coro, decrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECRBY")); -} - -static PHP_METHOD(swoole_redis_coro, decr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECR")); -} - -static PHP_METHOD(swoole_redis_coro, getBit) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETBIT")); -} - -static PHP_METHOD(swoole_redis_coro, lInsert) { - char *key, *pos; - size_t key_len, pos_len; - zval *z_val, *z_pivot; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { - return; - } - - if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { - php_swoole_error(E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[5]; - char *argv[5]; - - SW_REDIS_COMMAND_ARGV_FILL("LINSERT", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(pos, pos_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_pivot) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 5, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, lGet) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LINDEX")); -} - -static PHP_METHOD(swoole_redis_coro, setTimeout) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, pexpire) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, expireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, pexpireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, move) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MOVE")); -} - -static PHP_METHOD(swoole_redis_coro, select) { - zend_long db_number; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(db_number) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_long(zsetting, "database", db_number); - RETURN_BOOL(redis_select_db(redis, db_number)); -} - -static PHP_METHOD(swoole_redis_coro, getRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, listTrim) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LTRIM")); -} - -static PHP_METHOD(swoole_redis_coro, lGetRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, lRem) { - char *key; - size_t key_len; - zend_long count = 0; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, &z_val, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("LREM", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) count); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYRANK")); -} - -static PHP_METHOD(swoole_redis_coro, incrByFloat) { - sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBYFLOAT")); -} - -static PHP_METHOD(swoole_redis_coro, bitCount) { - char *key; - size_t key_len; - zend_long start = 0, end = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &key, &key_len, &start, &end) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("BITCOUNT", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) start); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%d", (int) end); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, bitOp) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 3 || - SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - return; - } - - int j, i = 0; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BITOP", 5) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - for (j = 1; j < argc - 1; j++) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); -} - -static PHP_METHOD(swoole_redis_coro, sMove) { - char *src, *dst; - size_t src_len, dst_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &src, &src_len, &dst, &dst_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("SMOVE", 5) - SW_REDIS_COMMAND_ARGV_FILL(src, src_len) - SW_REDIS_COMMAND_ARGV_FILL(dst, dst_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, sAdd) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SADD")); -} - -static PHP_METHOD(swoole_redis_coro, sRemove) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SREM")); -} - -static PHP_METHOD(swoole_redis_coro, zDelete) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREM")); -} - -static sw_inline void redis_subscribe(INTERNAL_FUNCTION_PARAMETERS, const char *cmd) { - zval *z_arr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - if (redis->defer) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "subscribe cannot be used with defer enabled"); - RETURN_FALSE; - } - - HashTable *ht_chan = Z_ARRVAL_P(z_arr); - size_t chan_num = zend_hash_num_elements(ht_chan); - int argc = 1 + chan_num, i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_REDIS_COMMAND_ARGV_FILL(cmd, strlen(cmd)); - - zval *value; - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis->defer = true; - redis_request(redis, argc, argv, argvlen, return_value); - redis->defer = false; - SW_REDIS_COMMAND_FREE_ARGV - - if (Z_TYPE_P(return_value) == IS_TRUE) { - redis->session.subscribe = true; - } -} - -static PHP_METHOD(swoole_redis_coro, subscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, unsubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pUnSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, multi) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MULTI")); -} - -static PHP_METHOD(swoole_redis_coro, exec) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXEC")); -} - -static PHP_METHOD(swoole_redis_coro, request) { - SW_REDIS_COMMAND_CHECK - - zval *params = nullptr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", ¶ms) == FAILURE) { - RETURN_FALSE; - } - - int argc = zend_hash_num_elements(Z_ARRVAL_P(params)); - int i = 0; - zval *value; - - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(params), value) - if (i == argc) { - break; - } - zend_string *convert_str = zval_get_string(value); - argvlen[i] = ZSTR_LEN(convert_str); - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - i++; - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, eval) { - char *script; - size_t script_len; - zval *params = nullptr; - zend_long keys_num = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &script, &script_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVAL", 4) - SW_REDIS_COMMAND_ARGV_FILL(script, script_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, ZEND_LONG_FMT, keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params_ht) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, evalSha) { - char *sha; - size_t sha_len; - zval *params = nullptr; - long keys_num = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &sha, &sha_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVALSHA", 7) - SW_REDIS_COMMAND_ARGV_FILL(sha, sha_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, "%ld", keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, script) { - int argc = ZEND_NUM_ARGS(); - if (argc < 1) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0; - if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "flush") || - !strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "kill")) { - size_t argvlen[2]; - char *argv[2]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - redis_request(redis, 2, argv, argvlen, return_value); - efree(z_args); - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "exists")) { - if (argc < 2) { - efree(z_args); - RETURN_FALSE; - } else { - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (argc + 1)); - char **argv = (char **) emalloc(sizeof(char *) * (argc + 1)); - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("EXISTS", 6) - int j = 1; - for (; j < argc; j++) { - zend_string *z_arg_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(z_arg_str), ZSTR_LEN(z_arg_str)) - zend_string_release(z_arg_str); - } - - redis_request(redis, argc + 1, argv, argvlen, return_value); - efree(argvlen); - efree(argv); - efree(z_args); - } - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "load")) { - if (argc < 2 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } else { - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("LOAD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[1]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[1])) - redis_request(redis, 3, argv, argvlen, return_value); - efree(z_args); - } - } else { - efree(z_args); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, xLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XLEN")); -} - -static PHP_METHOD(swoole_redis_coro, xAdd) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - char *key, *id; - size_t key_len, id_len; - zval *z_arr; - int argc, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|a", &key, &key_len, &id, &id_len, &z_arr, &z_options) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 3; - zval *value; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // NOMKSTREAM - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("nomkstream"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOMKSTREAM", 10) - options_argc++; - } - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // id - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - // k-v - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRead) { - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_streams, &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc * 2 + 2; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREAD", 5) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xRevRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XREVRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xTrim) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - int i = 0, argc = 2, options_argc = 0; - char buf[32], *key; - size_t buf_len, key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_options) == FAILURE) { - RETURN_FALSE; - } - if (php_swoole_array_length_safe(z_options) < 1) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XTRIM", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xDel) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XDEL")); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreate) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - zend_bool mkstream = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|b", &key, &key_len, &group_name, &group_name_len, &id, &id_len, &mkstream) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[6]; - char *argv[6]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - if (mkstream) { - SW_REDIS_COMMAND_ARGV_FILL("MKSTREAM", 8) - argc = 6; - } - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupSetId) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &id, &id_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDestroy) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DESTROY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DELCONSUMER", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xReadGroup) { - char *group_name, *consumer_name; - size_t group_name_len, consumer_name_len; - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - char buf[32]; - size_t buf_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|a", &group_name, &group_name_len, &consumer_name, &consumer_name_len, &z_streams, &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc = argc * 2 + 5; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREADGROUP", 10) - SW_REDIS_COMMAND_ARGV_FILL("GROUP", 5) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // NOACK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("noack"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOACK", 5) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xPending) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 3, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &key, &key_len, &group_name, &group_name_len, &z_options) == FAILURE) { - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XPENDING", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // START - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("start"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // END - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("end"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc++; - } - // CONSUMER - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("consumer"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAck) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_id = nullptr; - int i = 0, argc = 3, id_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &key_len, &group_name, &group_name_len, &z_id) == FAILURE) { - RETURN_FALSE; - } - if ((id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id))) == 0) { - RETURN_FALSE; - } - argc += id_argc; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XACK", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xClaim) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - zend_long min_idle_time = 0; - zval *z_id = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 5, id_argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssla|a", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len, &min_idle_time, &z_id, &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id)); - argc += id_argc; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XCLAIM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // TIME - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("time"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("TIME", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // RETRYCOUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("retrycount"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("RETRYCOUNT", 10) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // FORCE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("force"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("FORCE", 5) - options_argc++; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAutoClaim) { - char *key, *group_name, *consumer_name, *start; - size_t key_len, group_name_len, consumer_name_len, start_len; - zend_long min_idle_time = 0; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 6, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssls|a", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len, &min_idle_time, &start, &start_len, &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XAUTOCLAIM", 10) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xInfoConsumers) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("CONSUMERS", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoGroups) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("GROUPS", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoStream) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("STREAM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply) { - int j; - zval _val, *val = &_val; - - switch (reply->type) { - case REDIS_REPLY_INTEGER: - ZVAL_LONG(return_value, reply->integer); - break; - - case REDIS_REPLY_DOUBLE: - ZVAL_DOUBLE(return_value, reply->dval); - break; - - case REDIS_REPLY_BOOL: - ZVAL_BOOL(return_value, reply->integer); - break; - - case REDIS_REPLY_ERROR: - ZVAL_FALSE(return_value); - if (redis->context->err == 0) { - if (strncmp(reply->str, "NOAUTH", 6) == 0) { - redis->context->err = SW_REDIS_ERR_NOAUTH; - } else { - redis->context->err = SW_REDIS_ERR_OTHER; - } - size_t str_len = strlen(reply->str); - memcpy(redis->context->errstr, reply->str, SW_MIN(str_len, sizeof(redis->context->errstr) - 1)); - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - break; - - case REDIS_REPLY_STATUS: - if (redis->context->err == 0) { - if (reply->len > 0) { - if (strncmp(reply->str, "OK", 2) == 0) { - ZVAL_TRUE(return_value); - break; - } - long l; - if (strncmp(reply->str, "string", 6) == 0) { - l = SW_REDIS_TYPE_STRING; - } else if (strncmp(reply->str, "set", 3) == 0) { - l = SW_REDIS_TYPE_SET; - } else if (strncmp(reply->str, "list", 4) == 0) { - l = SW_REDIS_TYPE_LIST; - } else if (strncmp(reply->str, "zset", 4) == 0) { - l = SW_REDIS_TYPE_ZSET; - } else if (strncmp(reply->str, "hash", 4) == 0) { - l = SW_REDIS_TYPE_HASH; - } else { - l = SW_REDIS_TYPE_NOT_FOUND; - } - ZVAL_LONG(return_value, l); - } else { - ZVAL_TRUE(return_value); - } - } else { - ZVAL_FALSE(return_value); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - } - break; - - case REDIS_REPLY_STRING: - if (redis->serialize) { - char *reserve_str = reply->str; - php_unserialize_data_t s_ht; - PHP_VAR_UNSERIALIZE_INIT(s_ht); - if (!php_var_unserialize(return_value, - (const unsigned char **) &reply->str, - (const unsigned char *) reply->str + reply->len, - &s_ht)) { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - PHP_VAR_UNSERIALIZE_DESTROY(s_ht); - reply->str = reserve_str; - } else { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - break; - - case REDIS_REPLY_ARRAY: - array_init(return_value); - for (j = 0; j < (int) reply->elements; j++) { - swoole_redis_coro_parse_result(redis, val, reply->element[j]); - (void) add_next_index_zval(return_value, val); - } - break; - - case REDIS_REPLY_NIL: - default: - ZVAL_NULL(return_value); - return; - } -} diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 944a1625c03..7ecaa79b765 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,19 +22,23 @@ BEGIN_EXTERN_C() #include "ext/standard/php_string.h" +#include "stubs/php_swoole_redis_server_arginfo.h" END_EXTERN_C() -using swoole::Server; -using swoole::RecvData; -using swoole::ListenPort; using swoole::Connection; +using swoole::ListenPort; +using swoole::RecvData; +using swoole::Server; +using swoole::String; namespace Redis = swoole::redis; zend_class_entry *swoole_redis_server_ce; zend_object_handlers swoole_redis_server_handlers; -static std::unordered_map redis_handlers; +static SW_THREAD_LOCAL std::unordered_map redis_handlers; + +static bool redis_response_format(String *buf, zend_long type, zval *value); SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_redis_server, setHandler); @@ -43,36 +47,18 @@ static PHP_METHOD(swoole_redis_server, format); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_setHandler, 0, 0, 2) - ZEND_ARG_INFO(0, command) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_getHandler, 0, 0, 1) - ZEND_ARG_INFO(0, command) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_format, 0, 0, 1) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_redis_server_methods[] = { - PHP_ME(swoole_redis_server, setHandler, arginfo_swoole_redis_server_setHandler, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_server, getHandler, arginfo_swoole_redis_server_getHandler, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_server, format, arginfo_swoole_redis_server_format, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_redis_server, setHandler, arginfo_class_Swoole_Redis_Server_setHandler, ZEND_ACC_PUBLIC) + PHP_ME(swoole_redis_server, getHandler, arginfo_class_Swoole_Redis_Server_getHandler, ZEND_ACC_PUBLIC) + PHP_ME(swoole_redis_server, format, arginfo_class_Swoole_Redis_Server_format, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on void php_swoole_redis_server_minit(int module_number) { - SW_INIT_CLASS_ENTRY_EX(swoole_redis_server, - "Swoole\\Redis\\Server", - "swoole_redis_server", - nullptr, - swoole_redis_server_methods, - swoole_server); + SW_INIT_CLASS_ENTRY_EX( + swoole_redis_server, "Swoole\\Redis\\Server", nullptr, swoole_redis_server_methods, swoole_server); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_server); SW_SET_CLASS_CLONEABLE(swoole_redis_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_server, sw_zend_class_unset_property_deny); @@ -88,7 +74,7 @@ void php_swoole_redis_server_minit(int module_number) { void php_swoole_redis_server_rshutdown() { for (auto i = redis_handlers.begin(); i != redis_handlers.end(); i++) { - sw_zend_fci_cache_discard(&i->second); + sw_callable_free(i->second); } redis_handlers.clear(); } @@ -173,7 +159,11 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%.*s", command_len, command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif auto i = redis_handlers.find(std::string(_command, _command_len)); if (i == redis_handlers.end()) { @@ -182,14 +172,14 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { return serv->send(fd, err_msg, length) ? SW_OK : SW_ERR; } - zend_fcall_info_cache *fci_cache = &i->second; + auto fci_cache = i->second; zval args[2]; zval retval; ZVAL_LONG(&args[0], fd); args[1] = zparams; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, &retval, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 2, args, &retval, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onRequest with command '%.*s' handler error", ZSTR_VAL(swoole_redis_server_ce->name), @@ -212,37 +202,38 @@ static PHP_METHOD(swoole_redis_server, setHandler) { size_t command_len; zval *zcallback; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &command, &command_len, &zcallback) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(command, command_len) + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (command_len == 0 || command_len >= SW_REDIS_MAX_COMMAND_SIZE) { php_swoole_fatal_error(E_ERROR, "invalid command"); RETURN_FALSE; } - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { return; } - efree(func_name); char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif zend_update_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, zcallback); std::string key(_command, _command_len); auto i = redis_handlers.find(key); if (i != redis_handlers.end()) { - sw_zend_fci_cache_discard(&i->second); + sw_callable_free(i->second); } - sw_zend_fci_cache_persist(fci_cache); - redis_handlers[key] = *fci_cache; + redis_handlers[key] = fci_cache; RETURN_TRUE; } @@ -251,73 +242,74 @@ static PHP_METHOD(swoole_redis_server, getHandler) { char *command; size_t command_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &command, &command_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(command, command_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif zval rv; zval *handler = zend_read_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, 1, &rv); RETURN_ZVAL(handler, 1, 0); } -static PHP_METHOD(swoole_redis_server, format) { - zend_long type; - zval *value = nullptr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &type, &value) == FAILURE) { - RETURN_FALSE; +static void redis_response_format_array_item(String *buf, zval *item) { + switch (Z_TYPE_P(item)) { + case IS_LONG: + case IS_FALSE: + case IS_TRUE: + redis_response_format(buf, Redis::REPLY_INT, item); + break; + case IS_ARRAY: + if (zend_array_is_list(Z_ARRVAL_P(item))) { + redis_response_format(buf, Redis::REPLY_SET, item); + } else { + redis_response_format(buf, Redis::REPLY_MAP, item); + } + break; + default: + redis_response_format(buf, Redis::REPLY_STRING, item); + break; } +} - char message[256]; - int length; - - swoole::String *format_buffer = sw_tg_buffer(); - +static bool redis_response_format(String *buf, zend_long type, zval *value) { if (type == Redis::REPLY_NIL) { - RETURN_STRINGL(SW_REDIS_RETURN_NIL, sizeof(SW_REDIS_RETURN_NIL) - 1); - } else if (type == Redis::REPLY_STATUS) { + buf->append(SW_STRL(SW_REDIS_RETURN_NIL)); + } else if (type == Redis::REPLY_ERROR || type == Redis::REPLY_STATUS) { + char flag = type == Redis::REPLY_ERROR ? '-' : '+'; + const char *default_message = type == Redis::REPLY_ERROR ? "ERR" : "OK"; if (value) { zend::String str_value(value); - length = sw_snprintf(message, sizeof(message), "+%.*s\r\n", (int) str_value.len(), str_value.val()); + SW_STRING_FORMAT(buf, "%c%.*s\r\n", flag, (int) str_value.len(), str_value.val()); } else { - length = sw_snprintf(message, sizeof(message), "+%s\r\n", "OK"); + SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message); } - RETURN_STRINGL(message, length); - } else if (type == Redis::REPLY_ERROR) { - if (value) { - zend::String str_value(value); - length = sw_snprintf(message, sizeof(message), "-%.*s\r\n", (int) str_value.len(), str_value.val()); - } else { - length = sw_snprintf(message, sizeof(message), "-%s\r\n", "ERR"); - } - RETURN_STRINGL(message, length); } else if (type == Redis::REPLY_INT) { if (!value) { goto _no_value; } - length = sw_snprintf(message, sizeof(message), ":" ZEND_LONG_FMT "\r\n", zval_get_long(value)); - RETURN_STRINGL(message, length); + SW_STRING_FORMAT(buf, ":" ZEND_LONG_FMT "\r\n", zval_get_long(value)); } else if (type == Redis::REPLY_STRING) { if (!value) { _no_value: php_swoole_fatal_error(E_WARNING, "require more parameters"); - RETURN_FALSE; + return false; } zend::String str_value(value); if (str_value.len() > SW_REDIS_MAX_STRING_SIZE || str_value.len() < 1) { php_swoole_fatal_error(E_WARNING, "invalid string size"); - RETURN_FALSE; + return false; } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); - RETURN_STRINGL(format_buffer->str, format_buffer->length); + SW_STRING_FORMAT(buf, "$%zu\r\n", str_value.len()); + buf->append(str_value.val(), str_value.len()); + buf->append(SW_CRLF, SW_CRLF_LEN); } else if (type == Redis::REPLY_SET) { if (!value) { goto _no_value; @@ -325,20 +317,13 @@ static PHP_METHOD(swoole_redis_server, format) { if (!ZVAL_IS_ARRAY(value)) { php_swoole_fatal_error(E_WARNING, "the second parameter should be an array"); } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); - format_buffer->append(message, length); + SW_STRING_FORMAT(buf, "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); zval *item; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item) - zend::String str_value(item); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); - SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), item) { + redis_response_format_array_item(buf, item); + } + ZEND_HASH_FOREACH_END(); } else if (type == Redis::REPLY_MAP) { if (!value) { goto _no_value; @@ -346,30 +331,47 @@ static PHP_METHOD(swoole_redis_server, format) { if (!ZVAL_IS_ARRAY(value)) { php_swoole_fatal_error(E_WARNING, "the second parameter should be an array"); } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); - format_buffer->append(message, length); + SW_STRING_FORMAT(buf, "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); - char *key; - uint32_t keylen; - int keytype; + zend_string *key; + zend_ulong num_key; zval *item; - - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(value), key, keylen, keytype, item) - if (key == nullptr || keylen == 0) { - continue; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(value), num_key, key, item) { + if (key) { + SW_STRING_FORMAT(buf, "$%zu\r\n%.*s\r\n", ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key)); + } else { + std::string _key = std::to_string(num_key); + SW_STRING_FORMAT(buf, "$%zu\r\n%.*s\r\n", _key.length(), (int) _key.length(), _key.c_str()); + } + redis_response_format_array_item(buf, item); } - zend::String str_value(item); - length = sw_snprintf(message, sizeof(message), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); - (void) keytype; - SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); + ZEND_HASH_FOREACH_END(); } else { - php_swoole_error(E_WARNING, "Unknown type[" ZEND_LONG_FMT "]", type); + php_swoole_error(E_WARNING, "Unknown type[%d]", (int) type); + return false; + } + + return true; +} + +static PHP_METHOD(swoole_redis_server, format) { + zend_long type; + zval *value = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(type) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + auto buf = std::shared_ptr(swoole::make_string(1024, sw_zend_string_allocator())); + if (!redis_response_format(buf.get(), type, value)) { RETURN_FALSE; } + + auto str = zend::fetch_zend_string_by_val(buf->str); + buf->set_null_terminated(); + str->len = buf->length; + buf->release(); + RETURN_STR(str); } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index eb2347b7704..8bc3dd332eb 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -13,17 +13,40 @@ | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ + #include "php_swoole_cxx.h" + #include "swoole_socket.h" #include "swoole_util.h" #include "thirdparty/php/standard/proc_open.h" + #ifdef SW_USE_CURL -#include "thirdparty/php/curl/curl_interface.h" +#include "swoole_curl_interface.h" #endif #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_runtime_arginfo.h" + +#ifdef SW_USE_PGSQL +extern void swoole_pgsql_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_ODBC +extern void swoole_odbc_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_ORACLE +extern void swoole_oracle_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_SQLITE +extern void swoole_sqlite_set_blocking(bool blocking); +#endif +END_EXTERN_C() + /* openssl */ #ifndef OPENSSL_NO_ECDH #define HAVE_ECDH 1 @@ -40,9 +63,9 @@ using swoole::Coroutine; using swoole::PHPCoroutine; +using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::coroutine::PollSocket; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_runtime, enableCoroutine); @@ -55,8 +78,17 @@ static PHP_FUNCTION(swoole_time_sleep_until); static PHP_FUNCTION(swoole_stream_select); static PHP_FUNCTION(swoole_stream_socket_pair); static PHP_FUNCTION(swoole_user_func_handler); +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +static PHP_FUNCTION(swoole_putenv); +#endif +#if PHP_VERSION_ID >= 80400 +extern PHP_FUNCTION(swoole_exit); +#endif SW_EXTERN_C_END +static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length); +static void detach_parent_class(const char *child_name); +static void clear_class_entries(); static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam); static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count); static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count); @@ -66,19 +98,6 @@ static int socket_stat(php_stream *stream, php_stream_statbuf *ssb); static int socket_cast(php_stream *stream, int castas, void **ret); static bool socket_ssl_set_options(Socket *sock, php_stream_context *context); // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_enableCoroutine, 0, 0, 0) - ZEND_ARG_INFO(0, enable) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_setHookFlags, 0, 0, 1) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - static zend_class_entry *swoole_runtime_ce; static php_stream_ops socket_ops { @@ -86,22 +105,19 @@ static php_stream_ops socket_ops { socket_read, socket_close, socket_flush, - "tcp_socket/coroutine", + "socket/coroutine", nullptr, /* seek */ socket_cast, socket_stat, socket_set_option, }; -struct php_swoole_netstream_data_t { +struct NetStream { php_netstream_data_t stream; - Socket *socket; + std::shared_ptr socket; bool blocking; }; -static bool runtime_hook_init = false; -static int runtime_hook_flags = 0; - static struct { php_stream_transport_factory tcp; php_stream_transport_factory udp; @@ -120,16 +136,21 @@ static struct { static std::vector unsafe_functions { "pcntl_fork", + "pcntl_rfork", "pcntl_wait", "pcntl_waitpid", "pcntl_sigtimedwait", + "pcntl_sigwaitinfo", }; -static const zend_function_entry swoole_runtime_methods[] = -{ - PHP_ME(swoole_runtime, enableCoroutine, arginfo_swoole_runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_runtime, getHookFlags, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_runtime, setHookFlags, arginfo_swoole_runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +static std::unordered_map swoole_runtime_environ; +#endif + +static const zend_function_entry swoole_runtime_methods[] = { + PHP_ME(swoole_runtime, enableCoroutine, arginfo_class_Swoole_Runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_runtime, getHookFlags, arginfo_class_Swoole_Runtime_getHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_runtime, setHookFlags, arginfo_class_Swoole_Runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -137,7 +158,10 @@ static const zend_function_entry swoole_runtime_methods[] = static php_stream_wrapper ori_php_plain_files_wrapper; static php_stream_ops ori_php_stream_stdio_ops; -static void hook_func(const char *name, size_t l_name, zif_handler handler = nullptr, zend_internal_arg_info *arg_info = nullptr); +static void hook_func(const char *name, + size_t l_name, + zif_handler handler = nullptr, + zend_internal_arg_info *arg_info = nullptr); static void unhook_func(const char *name, size_t l_name); static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { @@ -148,11 +172,69 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { return zf->internal_function.arg_info; } +static zend_internal_arg_info *copy_arginfo(zend_function *zf, zend_internal_arg_info *_arg_info) { + uint32_t num_args = zf->internal_function.num_args + 1; + zend_internal_arg_info *arg_info = _arg_info - 1; + + auto new_arg_info = (zend_internal_arg_info *) pemalloc(sizeof(zend_internal_arg_info) * num_args, 1); + memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); + + if (zf->internal_function.fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + + for (uint32_t i = 0; i < num_args; i++) { + if (ZEND_TYPE_HAS_LIST(arg_info[i].type)) { + zend_type_list *old_list = ZEND_TYPE_LIST(arg_info[i].type); + zend_type_list *new_list = (zend_type_list *) pemalloc(ZEND_TYPE_LIST_SIZE(old_list->num_types), 1); + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); + ZEND_TYPE_SET_PTR(new_arg_info[i].type, new_list); + + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), 1); + ZEND_TYPE_SET_PTR(*list_type, name); + } + ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(arg_info[i].type)) { + zend_string *name = zend_string_dup(ZEND_TYPE_NAME(arg_info[i].type), 1); + ZEND_TYPE_SET_PTR(new_arg_info[i].type, name); + } + } + + return new_arg_info + 1; +} + +static void free_arg_info(zend_internal_function *function) { + if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_HAS_TYPE_HINTS)) && function->arg_info) { + uint32_t i; + uint32_t num_args = function->num_args + 1; + zend_internal_arg_info *arg_info = function->arg_info - 1; + + if (function->fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + for (i = 0; i < num_args; i++) { + zend_type_release(arg_info[i].type, /* persistent */ 1); + } + free(arg_info); + } +} + #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) -#define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) +#define SW_HOOK_WITH_NATIVE_FUNC(f) \ + hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) +#define SW_HOOK_WITH_PHP_FUNC(f) hook_func(ZEND_STRL(#f)) + +#define SW_HOOK_LIBRARY_FE(name, arg_info) \ + ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) -static zend_array *tmp_function_table = nullptr; +static SW_THREAD_LOCAL int runtime_hook_flags = 0; +static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; +static SW_THREAD_LOCAL std::unordered_map child_class_entries; +static zend::ConcurrencyHashMap ori_func_handlers(nullptr); +static zend::ConcurrencyHashMap ori_func_arg_infos(nullptr); SW_EXTERN_C_BEGIN #include "ext/standard/file.h" @@ -160,8 +242,7 @@ SW_EXTERN_C_BEGIN SW_EXTERN_C_END void php_swoole_runtime_minit(int module_number) { - SW_INIT_CLASS_ENTRY_BASE( - swoole_runtime, "Swoole\\Runtime", "swoole_runtime", nullptr, swoole_runtime_methods, nullptr); + SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, "Swoole\\Runtime", nullptr, swoole_runtime_methods, nullptr); SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", PHPCoroutine::HOOK_TCP); @@ -171,7 +252,8 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SSL", PHPCoroutine::HOOK_SSL); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TLS", PHPCoroutine::HOOK_TLS); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_FUNCTION", PHPCoroutine::HOOK_STREAM_FUNCTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", PHPCoroutine::HOOK_STREAM_FUNCTION); // backward compatibility + // backward compatibility + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", PHPCoroutine::HOOK_STREAM_FUNCTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_FILE", PHPCoroutine::HOOK_FILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STDIO", PHPCoroutine::HOOK_STDIO); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SLEEP", PHPCoroutine::HOOK_SLEEP); @@ -180,6 +262,18 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_NATIVE_CURL", PHPCoroutine::HOOK_NATIVE_CURL); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_BLOCKING_FUNCTION", PHPCoroutine::HOOK_BLOCKING_FUNCTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SOCKETS", PHPCoroutine::HOOK_SOCKETS); +#ifdef SW_USE_PGSQL + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_PGSQL", PHPCoroutine::HOOK_PDO_PGSQL); +#endif +#ifdef SW_USE_ODBC + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ODBC", PHPCoroutine::HOOK_PDO_ODBC); +#endif +#ifdef SW_USE_ORACLE + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ORACLE", PHPCoroutine::HOOK_PDO_ORACLE); +#endif +#ifdef SW_USE_SQLITE + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_SQLITE", PHPCoroutine::HOOK_PDO_SQLITE); +#endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL); #ifdef SW_USE_CURL swoole_native_curl_minit(module_number); @@ -191,18 +285,52 @@ struct real_func { zend_function *function; zif_handler ori_handler; zend_internal_arg_info *ori_arg_info; + zend_internal_arg_info *arg_info_copy; uint32_t ori_fn_flags; uint32_t ori_num_args; - zend_fcall_info_cache *fci_cache; + zend::Callable *fci_cache; zval name; }; void php_swoole_runtime_rinit() { tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); + +#if defined(HAVE_PUTENV) && defined(SW_THREAD) + /** + * There are issues with the implementation of putenv in PHP, + * which can lead to memory invalid read in multi-thread environment. + */ + SW_HOOK_FUNC(putenv); +#endif + + if (!sw_is_main_thread()) { + return; + } + +#if PHP_VERSION_ID >= 80400 + SW_HOOK_FUNC(exit); +#endif + + HashTable *xport_hash = php_stream_xport_get_hash(); + ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); + ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); + ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); + ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); + ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); + ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); + + memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); } void php_swoole_runtime_rshutdown() { + if (sw_is_main_thread()) { + PHPCoroutine::disable_hook(); + ori_func_handlers.clear(); + ori_func_arg_infos.clear(); + } + void *ptr; ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) { real_func *rf = reinterpret_cast(ptr); @@ -211,16 +339,21 @@ void php_swoole_runtime_rshutdown() { */ if (rf->fci_cache) { zval_dtor(&rf->name); - efree(rf->fci_cache); + sw_callable_free(rf->fci_cache); + } + if (sw_is_main_thread()) { + rf->function->internal_function.handler = rf->ori_handler; + rf->function->internal_function.arg_info = rf->ori_arg_info; } - rf->function->internal_function.handler = rf->ori_handler; - rf->function->internal_function.arg_info = rf->ori_arg_info; efree(rf); } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(tmp_function_table); efree(tmp_function_table); tmp_function_table = nullptr; + + clear_class_entries(); } void php_swoole_runtime_mshutdown() { @@ -265,19 +398,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po } static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) { - php_swoole_netstream_data_t *abstract; - Socket *sock; + NetStream *abstract; ssize_t didwrite = -1; + std::shared_ptr sock; - abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - goto _exit; - } - - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; if (abstract->blocking) { didwrite = sock->send_all(buf, count); @@ -302,7 +431,7 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ } if (didwrite < 0) { - if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) { + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_write_error(sock->errCode) == SW_WAIT) { didwrite = 0; } else { stream->eof = 1; @@ -312,32 +441,22 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ } _exit: -#if PHP_VERSION_ID < 70400 - if (didwrite < 0) { - didwrite = 0; - } -#endif return didwrite; } -static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) -{ - php_swoole_netstream_data_t *abstract; - Socket *sock; +static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) { + std::shared_ptr sock; + NetStream *abstract; ssize_t nr_bytes = -1; - abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - goto _exit; - } - - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; if (abstract->blocking) { - nr_bytes = sock->recv(buf, count); + nr_bytes = sock->recv(buf, count); } else { nr_bytes = sock->get_socket()->recv(buf, count, 0); sock->set_err(errno); @@ -348,7 +467,7 @@ static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count } if (nr_bytes < 0) { - if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) { + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_read_error(sock->errCode) == SW_WAIT) { nr_bytes = 0; } else { stream->eof = 1; @@ -358,11 +477,6 @@ static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count } _exit: -#if PHP_VERSION_ID < 70400 - if (nr_bytes < 0) { - nr_bytes = 0; - } -#endif return nr_bytes; } @@ -371,24 +485,21 @@ static int socket_flush(php_stream *stream) { } static int socket_close(php_stream *stream, int close_handle) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract)) { return FAILURE; } /** set it null immediately */ stream->abstract = nullptr; - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { - return FAILURE; - } /** * it's always successful (even if the destructor rule is violated) * every calls passes through the hook function in PHP * so there is unnecessary to worry about the null pointer. */ - sock->close(); - delete sock; - efree(abstract); + if (abstract->socket) { + abstract->socket->close(); + } + delete abstract; return SUCCESS; } @@ -408,15 +519,11 @@ enum { enum { STREAM_XPORT_CRYPTO_OP_SETUP, STREAM_XPORT_CRYPTO_OP_ENABLE }; static int socket_cast(php_stream *stream, int castas, void **ret) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - return FAILURE; - } - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + NetStream *abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { return FAILURE; } - + std::shared_ptr sock = abstract->socket; switch (castas) { case PHP_STREAM_AS_STDIO: if (ret) { @@ -438,15 +545,14 @@ static int socket_cast(php_stream *stream, int castas, void **ret) { } static int socket_stat(php_stream *stream, php_stream_statbuf *ssb) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract)) { return FAILURE; } - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + if (UNEXPECTED(!abstract->socket)) { return FAILURE; } - return zend_fstat(sock->get_fd(), &ssb->sb); + return zend_fstat(abstract->socket->get_fd(), &ssb->sb); } static inline int socket_connect(php_stream *stream, Socket *sock, php_stream_xport_param *xparam) { @@ -565,14 +671,13 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo sock->set_timeout(timeout, Socket::TIMEOUT_READ); } - Socket *clisock = sock->accept(); + std::shared_ptr clisock(sock->accept()); #ifdef SW_USE_OPENSSL if (clisock != nullptr && clisock->ssl_is_enable()) { if (!clisock->ssl_handshake()) { sock->errCode = clisock->errCode; - delete clisock; - clisock = nullptr; + clisock.reset(); } } #endif @@ -593,9 +698,7 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo clisock->get_socket()->set_tcp_nodelay(tcp_nodelay); } #endif - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) emalloc(sizeof(*abstract)); - memset(abstract, 0, sizeof(*abstract)); - + auto abstract = new NetStream(); abstract->socket = clisock; abstract->blocking = true; @@ -606,7 +709,7 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo GC_ADDREF(stream->ctx); } } - return 0; + return SUCCESS; } } @@ -715,12 +818,12 @@ static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) zval argv[1]; ZVAL_STRINGL(&argv[0], peer_cert.c_str(), peer_cert.length()); - zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv); + auto retval = zend::function::call("openssl_x509_read", 1, argv); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &retval.value); zval_dtor(&argv[0]); - if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "capture_peer_cert_chain")) - && zend_is_true(val)) { + if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "capture_peer_cert_chain")) && + zend_is_true(val)) { zval arr; auto chain = sslsock->get_socket()->ssl_get_peer_cert_chain(INT_MAX); @@ -729,7 +832,7 @@ static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) for (auto &cert : chain) { zval argv[1]; ZVAL_STRINGL(&argv[0], cert.c_str(), cert.length()); - zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv); + auto retval = zend::function::call("openssl_x509_read", 1, argv); zval_add_ref(&retval.value); add_next_index_zval(&arr, &retval.value); zval_dtor(&argv[0]); @@ -749,28 +852,25 @@ static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xpo php_stream_context *context = PHP_STREAM_CONTEXT(stream); if (cparam->inputs.activate && !sock->ssl_is_available()) { sock->enable_ssl_encrypt(); - if (!sock->ssl_check_context()) { - return -1; - } if (!socket_ssl_set_options(sock, context)) { return -1; } if (!sock->ssl_handshake()) { return -1; } - return 0; } else if (!cparam->inputs.activate && sock->ssl_is_available()) { - return sock->ssl_shutdown() ? 0 : -1; + sock->ssl_shutdown(); + return -1; } - if (context) { + if (context && sock->ssl_is_available()) { zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { return -1; } } - return 0; + return 1; } #endif @@ -786,8 +886,8 @@ static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_ case STREAM_XPORT_OP_CONNECT_ASYNC: xparam->outputs.returncode = socket_connect(stream, sock, xparam); #ifdef SW_USE_OPENSSL - if (sock->ssl_is_enable() - && (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) { + if (sock->ssl_is_enable() && + (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) { xparam->outputs.returncode = -1; } #endif @@ -890,11 +990,12 @@ static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_ } static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract || !abstract->socket)) { return PHP_STREAM_OPTION_RETURN_ERR; } - Socket *sock = (Socket *) abstract->socket; + std::shared_ptr sock_wrapped = abstract->socket; + auto sock = sock_wrapped.get(); switch (option) { case PHP_STREAM_OPTION_BLOCKING: if (abstract->blocking == (bool) value) { @@ -1002,7 +1103,6 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { if (sock->ssl_is_enable() && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), "ssl", ztmp) && ZVAL_IS_ARRAY(ztmp)) { - zval zalias; array_init(&zalias); zend_array *options = Z_ARRVAL_P(ztmp); @@ -1010,8 +1110,7 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { auto add_alias = [&zalias, options](const char *name, const char *alias) { zval *ztmp; if (php_swoole_array_get_value_ex(options, name, ztmp)) { - add_assoc_zval_ex(&zalias, alias, strlen(alias), ztmp); - zval_add_ref(ztmp); + zend::array_set(&zalias, alias, strlen(alias), ztmp); } }; @@ -1026,11 +1125,9 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { add_alias("verify_depth", "ssl_verify_depth"); add_alias("disable_compression", "ssl_disable_compression"); - php_swoole_socket_set_ssl(sock, &zalias); - if (!sock->ssl_check_context()) { - return false; - } + bool ret = php_swoole_socket_set_ssl(sock, &zalias); zval_dtor(&zalias); + return ret; } #endif } @@ -1038,6 +1135,39 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { return true; } +static php_stream *socket_create_original(const char *proto, + size_t protolen, + const char *resourcename, + size_t resourcenamelen, + const char *persistent_id, + int options, + int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC) { + php_stream_transport_factory factory = nullptr; + if (SW_STREQ(proto, protolen, "tcp")) { + factory = ori_factory.tcp; + } else if (SW_STREQ(proto, protolen, "ssl")) { + factory = ori_factory.ssl; + } else if (SW_STREQ(proto, protolen, "tls")) { + factory = ori_factory.tls; + } else if (SW_STREQ(proto, protolen, "unix")) { + factory = ori_factory._unix; + } else if (SW_STREQ(proto, protolen, "udp")) { + factory = ori_factory.udp; + } else if (SW_STREQ(proto, protolen, "udg")) { + factory = ori_factory.udg; + } + + if (factory) { + return factory( + proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC); + } else { + php_swoole_fatal_error(E_WARNING, "unknown protocol '%s'", proto); + return nullptr; + } +} + static php_stream *socket_create(const char *proto, size_t protolen, const char *resourcename, @@ -1048,13 +1178,15 @@ static php_stream *socket_create(const char *proto, struct timeval *timeout, php_stream_context *context STREAMS_DC) { php_stream *stream = nullptr; - php_swoole_netstream_data_t *abstract = nullptr; - Socket *sock; + Socket *sock = nullptr; - Coroutine::get_current_safe(); + auto co = Coroutine::get_current(); + if (sw_unlikely(co == nullptr)) { + return socket_create_original( + proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC); + } if (SW_STREQ(proto, protolen, "tcp")) { - _tcp: sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP); } else if (SW_STREQ(proto, protolen, "ssl") || SW_STREQ(proto, protolen, "tls")) { #ifdef SW_USE_OPENSSL @@ -1072,8 +1204,8 @@ static php_stream *socket_create(const char *proto, } else if (SW_STREQ(proto, protolen, "udg")) { sock = new Socket(SW_SOCK_UNIX_DGRAM); } else { - /* abort? */ - goto _tcp; + php_swoole_fatal_error(E_WARNING, "unknown protocol '%s'", proto); + return nullptr; } if (UNEXPECTED(sock->get_fd() < 0)) { @@ -1088,15 +1220,14 @@ static php_stream *socket_create(const char *proto, sock->set_zero_copy(true); - abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract)); - abstract->socket = sock; + auto abstract = new NetStream(); + abstract->socket.reset(sock); abstract->stream.socket = sock->get_fd(); abstract->blocking = true; - persistent_id = nullptr; // prevent stream api in user level using pconnect to persist the socket stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+"); - if (stream == nullptr) { + delete abstract; goto _failed; } @@ -1112,18 +1243,18 @@ static ZEND_FUNCTION(swoole_display_disabled_function) { } static bool disable_func(const char *name, size_t l_name) { - real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name); + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name); if (rf) { rf->function->internal_function.handler = ZEND_FN(swoole_display_disabled_function); return true; } - zend_function *zf = (zend_function*) zend_hash_str_find_ptr(EG(function_table), name, l_name); + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); if (zf == nullptr) { return false; } - rf = (real_func*) emalloc(sizeof(real_func)); + rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; rf->ori_handler = zf->internal_function.handler; @@ -1141,7 +1272,7 @@ static bool disable_func(const char *name, size_t l_name) { } static bool enable_func(const char *name, size_t l_name) { - real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name); + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name); if (!rf) { return false; } @@ -1154,6 +1285,16 @@ static bool enable_func(const char *name, size_t l_name) { return true; } +bool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS) { + auto ori_handler = php_swoole_get_original_handler(name, len); + if (!ori_handler) { + return false; + } + ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + + return true; +} + void PHPCoroutine::disable_unsafe_function() { for (auto &f : unsafe_functions) { disable_func(f.c_str(), f.length()); @@ -1166,28 +1307,50 @@ void PHPCoroutine::enable_unsafe_function() { } } -bool PHPCoroutine::enable_hook(uint32_t flags) { - if (!runtime_hook_init) { - HashTable *xport_hash = php_stream_xport_get_hash(); - // php_stream - ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); - ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); - ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); - ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); - ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); - ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); - - // file - memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); - - runtime_hook_init = true; - } - // php_stream +static void hook_stream_throw_exception(const char *type) { + zend_throw_exception_ex( + swoole_exception_ce, SW_ERROR_PHP_FATAL_ERROR, "failed to register `%s` stream transport factory", type); +} + +static void hook_remove_stream_flags(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; + // stream factory + if (flags & PHPCoroutine::HOOK_TCP) { + flags ^= PHPCoroutine::HOOK_TCP; + } + if (flags & PHPCoroutine::HOOK_UDP) { + flags ^= PHPCoroutine::HOOK_UDP; + } + if (flags & PHPCoroutine::HOOK_UNIX) { + flags ^= PHPCoroutine::HOOK_UNIX; + } + if (flags & PHPCoroutine::HOOK_UDG) { + flags ^= PHPCoroutine::HOOK_UDG; + } + if (flags & PHPCoroutine::HOOK_SSL) { + flags ^= PHPCoroutine::HOOK_SSL; + } + if (flags & PHPCoroutine::HOOK_TLS) { + flags ^= PHPCoroutine::HOOK_TLS; + } + // stream ops + if (flags & PHPCoroutine::HOOK_FILE) { + flags ^= PHPCoroutine::HOOK_FILE; + } + if (flags & PHPCoroutine::HOOK_STDIO) { + flags ^= PHPCoroutine::HOOK_STDIO; + } + *flags_ptr = flags; +} + +static void hook_stream_factory(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; + if (flags & PHPCoroutine::HOOK_TCP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) { if (php_stream_xport_register("tcp", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_TCP; + hook_stream_throw_exception("tcp"); } } } else { @@ -1199,6 +1362,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDP)) { if (php_stream_xport_register("udp", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UDP; + hook_stream_throw_exception("udp"); } } } else { @@ -1210,6 +1374,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UNIX)) { if (php_stream_xport_register("unix", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UNIX; + hook_stream_throw_exception("unix"); } } } else { @@ -1221,6 +1386,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDG)) { if (php_stream_xport_register("udg", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UDG; + hook_stream_throw_exception("udg"); } } } else { @@ -1232,6 +1398,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SSL)) { if (php_stream_xport_register("ssl", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_SSL; + hook_stream_throw_exception("ssl"); } } } else { @@ -1247,6 +1414,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_TLS)) { if (php_stream_xport_register("tls", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_TLS; + hook_stream_throw_exception("tls"); } } } else { @@ -1258,17 +1426,10 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } } - if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { - if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { - SW_HOOK_FUNC(stream_select); - SW_HOOK_FUNC(stream_socket_pair); - } - } else { - if (runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { - SW_UNHOOK_FUNC(stream_select); - SW_UNHOOK_FUNC(stream_socket_pair); - } - } + *flags_ptr = flags; +} + +static void hook_stream_ops(uint32_t flags) { // file if (flags & PHPCoroutine::HOOK_FILE) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { @@ -1289,6 +1450,68 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { memcpy((void *) &php_stream_stdio_ops, &ori_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); } } +} + +static void hook_pdo_driver(uint32_t flags) { +#ifdef SW_USE_PGSQL + if (flags & PHPCoroutine::HOOK_PDO_PGSQL) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL)) { + swoole_pgsql_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL) { + swoole_pgsql_set_blocking(1); + } + } +#endif +#ifdef SW_USE_ODBC + if (flags & PHPCoroutine::HOOK_PDO_ODBC) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC)) { + swoole_odbc_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC) { + swoole_odbc_set_blocking(1); + } + } +#endif +#ifdef SW_USE_ORACLE + if (flags & PHPCoroutine::HOOK_PDO_ORACLE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE)) { + swoole_oracle_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE) { + swoole_oracle_set_blocking(1); + } + } +#endif +#ifdef SW_USE_SQLITE + if (flags & PHPCoroutine::HOOK_PDO_SQLITE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE)) { + swoole_sqlite_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE) { + swoole_sqlite_set_blocking(1); + } + } +#endif +} + +static void hook_all_func(uint32_t flags) { + // stream func + if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { + SW_HOOK_FUNC(stream_select); + SW_HOOK_FUNC(stream_socket_pair); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { + SW_UNHOOK_FUNC(stream_select); + SW_UNHOOK_FUNC(stream_socket_pair); + } + } // sleep if (flags & PHPCoroutine::HOOK_SLEEP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) { @@ -1325,8 +1548,8 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION)) { hook_func(ZEND_STRL("gethostbyname"), PHP_FN(swoole_coroutine_gethostbyname)); - hook_func(ZEND_STRL("exec")); - hook_func(ZEND_STRL("shell_exec")); + SW_HOOK_WITH_PHP_FUNC(exec); + SW_HOOK_WITH_PHP_FUNC(shell_exec); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { @@ -1335,33 +1558,37 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(shell_exec); } } + // ext-sockets if (flags & PHPCoroutine::HOOK_SOCKETS) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) { - hook_func(ZEND_STRL("socket_create")); - hook_func(ZEND_STRL("socket_create_listen")); - hook_func(ZEND_STRL("socket_create_pair"), PHP_FN(swoole_coroutine_socketpair)); - hook_func(ZEND_STRL("socket_connect")); - hook_func(ZEND_STRL("socket_write")); - hook_func(ZEND_STRL("socket_read")); - hook_func(ZEND_STRL("socket_send")); - hook_func(ZEND_STRL("socket_recv")); - hook_func(ZEND_STRL("socket_sendto")); - hook_func(ZEND_STRL("socket_recvfrom")); - hook_func(ZEND_STRL("socket_bind")); - hook_func(ZEND_STRL("socket_listen")); - hook_func(ZEND_STRL("socket_accept")); - hook_func(ZEND_STRL("socket_getpeername")); - hook_func(ZEND_STRL("socket_getsockname")); - hook_func(ZEND_STRL("socket_getopt")); - hook_func(ZEND_STRL("socket_get_option")); - hook_func(ZEND_STRL("socket_setopt")); - hook_func(ZEND_STRL("socket_set_option")); - hook_func(ZEND_STRL("socket_set_block")); - hook_func(ZEND_STRL("socket_set_nonblock")); - hook_func(ZEND_STRL("socket_shutdown")); - hook_func(ZEND_STRL("socket_close")); - hook_func(ZEND_STRL("socket_clear_error")); - hook_func(ZEND_STRL("socket_last_error")); + SW_HOOK_WITH_PHP_FUNC(socket_create); + SW_HOOK_WITH_PHP_FUNC(socket_create_listen); + SW_HOOK_WITH_PHP_FUNC(socket_create_pair); + SW_HOOK_WITH_PHP_FUNC(socket_connect); + SW_HOOK_WITH_PHP_FUNC(socket_write); + SW_HOOK_WITH_PHP_FUNC(socket_read); + SW_HOOK_WITH_PHP_FUNC(socket_send); + SW_HOOK_WITH_PHP_FUNC(socket_recv); + SW_HOOK_WITH_PHP_FUNC(socket_sendto); + SW_HOOK_WITH_PHP_FUNC(socket_recvfrom); + SW_HOOK_WITH_PHP_FUNC(socket_bind); + SW_HOOK_WITH_PHP_FUNC(socket_listen); + SW_HOOK_WITH_PHP_FUNC(socket_accept); + SW_HOOK_WITH_PHP_FUNC(socket_getpeername); + SW_HOOK_WITH_PHP_FUNC(socket_getsockname); + SW_HOOK_WITH_PHP_FUNC(socket_getopt); + SW_HOOK_WITH_PHP_FUNC(socket_get_option); + SW_HOOK_WITH_PHP_FUNC(socket_setopt); + SW_HOOK_WITH_PHP_FUNC(socket_set_option); + SW_HOOK_WITH_PHP_FUNC(socket_set_block); + SW_HOOK_WITH_PHP_FUNC(socket_set_nonblock); + SW_HOOK_WITH_PHP_FUNC(socket_shutdown); + SW_HOOK_WITH_PHP_FUNC(socket_close); + SW_HOOK_WITH_PHP_FUNC(socket_clear_error); + SW_HOOK_WITH_PHP_FUNC(socket_last_error); + SW_HOOK_WITH_PHP_FUNC(socket_import_stream); + + inherit_class(ZEND_STRL("Swoole\\Coroutine\\Socket"), ZEND_STRL("Socket")); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { @@ -1390,40 +1617,44 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(socket_close); SW_UNHOOK_FUNC(socket_clear_error); SW_UNHOOK_FUNC(socket_last_error); + SW_UNHOOK_FUNC(socket_import_stream); + + detach_parent_class("Swoole\\Coroutine\\Socket"); } } #ifdef SW_USE_CURL + // curl native if (flags & PHPCoroutine::HOOK_NATIVE_CURL) { if (flags & PHPCoroutine::HOOK_CURL) { php_swoole_fatal_error(E_WARNING, "cannot enable both hooks HOOK_NATIVE_CURL and HOOK_CURL at same time"); flags ^= PHPCoroutine::HOOK_CURL; } if (!(runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL)) { - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_close); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_copy_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_errno); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_error); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_exec); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_getinfo); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_init); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt_array); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_reset); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_pause); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_escape); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_unescape); - - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_init); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_add_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_exec); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_errno); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_select); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_setopt); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_getcontent); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_info_read); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_remove_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_close); + SW_HOOK_WITH_NATIVE_FUNC(curl_close); + SW_HOOK_WITH_NATIVE_FUNC(curl_copy_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_errno); + SW_HOOK_WITH_NATIVE_FUNC(curl_error); + SW_HOOK_WITH_NATIVE_FUNC(curl_exec); + SW_HOOK_WITH_NATIVE_FUNC(curl_getinfo); + SW_HOOK_WITH_NATIVE_FUNC(curl_init); + SW_HOOK_WITH_NATIVE_FUNC(curl_setopt); + SW_HOOK_WITH_NATIVE_FUNC(curl_setopt_array); + SW_HOOK_WITH_NATIVE_FUNC(curl_reset); + SW_HOOK_WITH_NATIVE_FUNC(curl_pause); + SW_HOOK_WITH_NATIVE_FUNC(curl_escape); + SW_HOOK_WITH_NATIVE_FUNC(curl_unescape); + + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_init); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_add_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_exec); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_errno); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_select); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_setopt); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_getcontent); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_info_read); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_remove_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_close); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL) { @@ -1454,19 +1685,21 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } #endif - + // curl if (flags & PHPCoroutine::HOOK_CURL) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_CURL)) { - hook_func(ZEND_STRL("curl_init")); - hook_func(ZEND_STRL("curl_setopt")); - hook_func(ZEND_STRL("curl_setopt_array")); - hook_func(ZEND_STRL("curl_exec")); - hook_func(ZEND_STRL("curl_getinfo")); - hook_func(ZEND_STRL("curl_errno")); - hook_func(ZEND_STRL("curl_error")); - hook_func(ZEND_STRL("curl_reset")); - hook_func(ZEND_STRL("curl_close")); - hook_func(ZEND_STRL("curl_multi_getcontent")); + SW_HOOK_WITH_PHP_FUNC(curl_init); + SW_HOOK_WITH_PHP_FUNC(curl_setopt); + SW_HOOK_WITH_PHP_FUNC(curl_setopt_array); + SW_HOOK_WITH_PHP_FUNC(curl_exec); + SW_HOOK_WITH_PHP_FUNC(curl_getinfo); + SW_HOOK_WITH_PHP_FUNC(curl_errno); + SW_HOOK_WITH_PHP_FUNC(curl_error); + SW_HOOK_WITH_PHP_FUNC(curl_reset); + SW_HOOK_WITH_PHP_FUNC(curl_close); + SW_HOOK_WITH_PHP_FUNC(curl_multi_getcontent); + + inherit_class(ZEND_STRL("Swoole\\Curl\\Handler"), ZEND_STRL("CurlHandle")); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_CURL) { @@ -1480,10 +1713,43 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(curl_reset); SW_UNHOOK_FUNC(curl_close); SW_UNHOOK_FUNC(curl_multi_getcontent); + + detach_parent_class("Swoole\\Curl\\Handler"); + } + } +} + +bool PHPCoroutine::enable_hook(uint32_t flags) { + /** + * Stream-related settings are global variables, not thread-local resources. + * The child threads must not modify stream settings; + * the main thread can only make changes when there are no active worker threads. + */ + if (sw_is_main_thread()) { + if (sw_active_thread_count() > 1) { + swoole_warning( + "The stream runtime hook must be enabled or disabled only when there are no active threads."); + hook_remove_stream_flags(&flags); } + } else { + hook_remove_stream_flags(&flags); + } + + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); + } + + hook_stream_factory(&flags); + hook_stream_ops(flags); + hook_pdo_driver(flags); + hook_all_func(flags); + + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, &flags); } runtime_hook_flags = flags; + return true; } @@ -1496,44 +1762,19 @@ static PHP_METHOD(swoole_runtime, enableCoroutine) { php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode"); RETURN_FALSE; } - zval *zflags = nullptr; zend_long flags = PHPCoroutine::HOOK_ALL; - ZEND_PARSE_PARAMETERS_START(0, 2) + ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(zflags) // or zenable Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (zflags) { - if (Z_TYPE_P(zflags) == IS_LONG) { - flags = SW_MAX(0, Z_LVAL_P(zflags)); - } else if (ZVAL_IS_BOOL(zflags)) { - if (!Z_BVAL_P(zflags)) { - flags = 0; - } - } else { - const char *space, *class_name = get_active_class_name(&space); - zend_type_error("%s%s%s() expects parameter %d to be %s, %s given", - class_name, - space, - get_active_function_name(), - 1, - "bool or long", - zend_zval_type_name(zflags)); - } - } - PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } static PHP_METHOD(swoole_runtime, getHookFlags) { - if (runtime_hook_init) { - RETURN_LONG(runtime_hook_flags); - } else { - RETURN_LONG(PHPCoroutine::get_hook_flags()); - } + RETURN_LONG(PHPCoroutine::get_hook_flags()); } static PHP_METHOD(swoole_runtime, setHookFlags) { @@ -1564,7 +1805,7 @@ static PHP_FUNCTION(swoole_sleep) { if (Coroutine::get_current()) { RETURN_LONG(System::sleep((double) num) < 0 ? num : 0); } else { - RETURN_LONG(php_sleep(num)); + RETURN_LONG(php_sleep((unsigned int) num)); } } @@ -1740,17 +1981,45 @@ static PHP_FUNCTION(swoole_stream_select) { zval *r_array, *w_array, *e_array; zend_long sec, usec = 0; zend_bool secnull; +#if PHP_VERSION_ID >= 80100 + bool usecnull = 1; +#endif int retval = 0; ZEND_PARSE_PARAMETERS_START(4, 5) - Z_PARAM_ARRAY_EX(r_array, 1, 1) - Z_PARAM_ARRAY_EX(w_array, 1, 1) - Z_PARAM_ARRAY_EX(e_array, 1, 1) - Z_PARAM_LONG_EX(sec, secnull, 1, 0) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 1) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 1) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 1) + Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL +#if PHP_VERSION_ID >= 80100 + Z_PARAM_LONG_OR_NULL(usec, usecnull) +#else Z_PARAM_LONG(usec) +#endif ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#if PHP_VERSION_ID >= 80100 + if (secnull && !usecnull) { + if (usec != 0) { + zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null"); + RETURN_THROWS(); + } + } +#endif + + double timeout = -1; + if (!secnull) { + if (sec < 0) { + php_error_docref(nullptr, E_WARNING, "The seconds parameter must be greater than 0"); + RETURN_FALSE; + } else if (usec < 0) { + php_error_docref(nullptr, E_WARNING, "The microseconds parameter must be greater than 0"); + RETURN_FALSE; + } + timeout = (double) sec + ((double) usec / 1000000); + } + std::unordered_map fds; if (r_array != nullptr) { @@ -1770,17 +2039,12 @@ static PHP_FUNCTION(swoole_stream_select) { RETURN_FALSE; } - double timeout = -1; - if (!secnull) { - if (sec < 0) { - php_error_docref(nullptr, E_WARNING, "The seconds parameter must be greater than 0"); - RETURN_FALSE; - } else if (usec < 0) { - php_error_docref(nullptr, E_WARNING, "The microseconds parameter must be greater than 0"); - RETURN_FALSE; + ON_SCOPE_EXIT { + for (auto &i : fds) { + zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; + delete kv; } - timeout = (double) sec + ((double) usec / 1000000); - } + }; /* slight hack to support buffered data; if there is data sitting in the * read buffer of any of the streams in the read array, let's pretend @@ -1831,7 +2095,6 @@ static PHP_FUNCTION(swoole_stream_select) { } retval++; } - delete kv; } RETURN_LONG(retval); @@ -1859,40 +2122,42 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend if (zf == nullptr) { return; } -#if PHP_VERSION_ID < 80000 - if (zf->internal_function.handler == ZEND_FN(display_disabled_function)) { - return; - } -#endif + auto fn_str = zf->common.function_name; rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; + + auto fn_name = std::string(fn_str->val, fn_str->len); + rf->ori_handler = zf->internal_function.handler; rf->ori_arg_info = zf->internal_function.arg_info; + + if (sw_is_main_thread()) { + ori_func_handlers.set(fn_name, rf->ori_handler); + ori_func_arg_infos.set(fn_name, rf->ori_arg_info); + } + zf->internal_function.handler = handler; if (arg_info) { - zf->internal_function.arg_info = arg_info; + zf->internal_function.arg_info = copy_arginfo(zf, arg_info); + rf->arg_info_copy = zf->internal_function.arg_info; } if (use_php_func) { char func[128]; memcpy(func, ZEND_STRL("swoole_")); - memcpy(func + 7, zf->common.function_name->val, zf->common.function_name->len); - - ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7); + memcpy(func + 7, fn_str->val, fn_str->len); - char *func_name; - zend_fcall_info_cache *func_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(&rf->name, nullptr, 0, &func_name, nullptr, func_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); + ZVAL_STRINGL(&rf->name, func, fn_str->len + 7); + auto fci_cache = sw_callable_create(&rf->name); + if (!fci_cache) { return; } - efree(func_name); - rf->fci_cache = func_cache; + rf->fci_cache = fci_cache; } - zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf); + zend_hash_add_ptr(tmp_function_table, fn_str, rf); } static void unhook_func(const char *name, size_t l_name) { @@ -1900,28 +2165,28 @@ static void unhook_func(const char *name, size_t l_name) { if (rf == nullptr) { return; } + if (rf->arg_info_copy) { + free_arg_info(&rf->function->internal_function); + rf->arg_info_copy = nullptr; + } rf->function->internal_function.handler = rf->ori_handler; rf->function->internal_function.arg_info = rf->ori_arg_info; } php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) { - Socket *sock = new Socket(_fd, domain, type, protocol); - + auto *abstract = new NetStream(); + abstract->socket = std::make_shared(_fd, domain, type, protocol); if (FG(default_socket_timeout) > 0) { - sock->set_timeout((double) FG(default_socket_timeout)); + abstract->socket->set_timeout((double) FG(default_socket_timeout)); } - - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract)); - - abstract->socket = sock; abstract->stream.timeout.tv_sec = FG(default_socket_timeout); - abstract->stream.socket = sock->get_fd(); + abstract->stream.socket = abstract->socket->get_fd(); abstract->blocking = true; php_stream *stream = php_stream_alloc_rel(&socket_ops, abstract, nullptr, "r+"); if (stream == nullptr) { - delete sock; + delete abstract; } else { stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING; } @@ -1929,10 +2194,38 @@ php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, i return stream; } +php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC) { +#if PHP_VERSION_ID >= 80200 + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id, false STREAMS_CC); +#else + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id STREAMS_CC); +#endif +} + php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() { return &ori_php_stream_stdio_ops; } +zif_handler php_swoole_get_original_handler(const char *name, size_t len) { + if (sw_is_main_thread()) { + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, len); + if (rf) { + return rf->ori_handler; + } + } else { + zif_handler handler = ori_func_handlers.get(std::string(name, len)); + if (handler) { + return handler; + } + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, len); + if (zf && zf->type == ZEND_INTERNAL_FUNCTION && zf->internal_function.handler) { + return zf->internal_function.handler; + } + } + + return nullptr; +} + static PHP_FUNCTION(swoole_stream_socket_pair) { zend_long domain, type, protocol; php_stream *s1, *s2; @@ -1966,19 +2259,136 @@ static PHP_FUNCTION(swoole_stream_socket_pair) { } static PHP_FUNCTION(swoole_user_func_handler) { + auto fn_str = execute_data->func->common.function_name; + if (!swoole_coroutine_is_in()) { + auto ori_handler = ori_func_handlers.get(std::string(fn_str->val, fn_str->len)); + ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; + } + + real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); + if (!rf) { + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_UNDEFINED_BEHAVIOR, "%s func not exists", fn_str->val); + return; + } + zend_fcall_info fci; fci.size = sizeof(fci); fci.object = nullptr; - ZVAL_UNDEF(&fci.function_name); fci.retval = return_value; fci.param_count = ZEND_NUM_ARGS(); fci.params = ZEND_CALL_ARG(execute_data, 1); -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + zend_call_function(&fci, rf->fci_cache->ptr()); +} + +zend_class_entry *find_class_entry(const char *name, size_t length) { + zend_string *search_key = zend_string_init(name, length, 0); + zend_class_entry *class_ce = zend_lookup_class(search_key); + zend_string_release(search_key); + return class_ce ? class_ce : nullptr; +} + +static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length) { + zend_class_entry *temp_ce = nullptr; + zend_class_entry *child_ce = find_class_entry(child_name, child_length); + zend_class_entry *parent_ce = find_class_entry(parent_name, parent_length); + + if (!child_ce || !parent_ce || instanceof_function(child_ce, parent_ce)) { + return; + } + + temp_ce = child_ce; + while (temp_ce->parent) { + temp_ce = temp_ce->parent; + } + temp_ce->parent = parent_ce; + + std::string key(ZSTR_VAL(child_ce->name)); + child_class_entries.insert({key, child_ce}); +} + +void start_detach_parent_class(zend_class_entry *class_ce) { + zend_class_entry *p1 = nullptr; + zend_class_entry *p2 = nullptr; + + p1 = class_ce; + p2 = class_ce->parent; + while (p2->parent) { + p1 = p1->parent; + p2 = p2->parent; + } + + p1->parent = nullptr; +} + +static void detach_parent_class(const char *child_name) { + std::string search_key(child_name); + auto iter = child_class_entries.find(search_key); + if (iter == child_class_entries.end()) { + return; + } + start_detach_parent_class(iter->second); + child_class_entries.erase(search_key); +} + +static void clear_class_entries() { + for (auto iter = child_class_entries.begin(); iter != child_class_entries.end(); iter++) { + start_detach_parent_class(iter->second); + } + child_class_entries.clear(); +} + +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +/* {{{ Set the value of an environment variable */ +static PHP_FUNCTION(swoole_putenv) { + char *setting; + size_t setting_len; + char *p; + bool result; + std::string key; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(setting, setting_len) + ZEND_PARSE_PARAMETERS_END(); + + if (setting_len == 0 || setting[0] == '=') { + zend_argument_value_error(1, "must have a valid syntax"); + RETURN_THROWS(); + } + + if ((p = strchr(setting, '='))) { + key = std::string(setting, p - setting); + } else { + key = std::string(setting, setting_len); + } + + tsrm_env_lock(); + swoole_runtime_environ[key] = std::string(setting, setting_len); + auto iter = swoole_runtime_environ.find(key); + +#ifdef HAVE_UNSETENV + if (!p) { /* no '=' means we want to unset it */ + unsetenv(iter->second.c_str()); + } + if (!p || putenv((char *) iter->second.c_str()) == 0) { /* success */ #else - fci.no_separation = 1; + if (putenv((char *) iter->second.c_str()) == 0) { /* success */ +#endif + +#ifdef HAVE_TZSET + if (zend_binary_strcasecmp(key.c_str(), key.length(), ZEND_STRL("TZ")) == 0) { + tzset(); + } #endif + result = true; + } else { + result = false; + } - real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); - zend_call_function(&fci, rf->fci_cache); + tsrm_env_unlock(); + RETURN_BOOL(result); } +/* }}} */ +#endif diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 0d7c0fec2ba..da8d55951b9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -10,13 +10,15 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_server.h" #include "php_swoole_http_server.h" #include "php_swoole_process.h" +#include "php_swoole_thread.h" +#include "php_swoole_call_stack.h" #include "swoole_msg_queue.h" #include "ext/standard/php_var.h" @@ -26,6 +28,11 @@ #include #endif +BEGIN_EXTERN_C() +#include "ext/json/php_json.h" +#include "stubs/php_swoole_server_arginfo.h" +END_EXTERN_C() + using namespace swoole; struct ConnectionIterator { @@ -65,47 +72,45 @@ static std::unordered_map server_event_map({ static void php_swoole_server_onStart(Server *); static void php_swoole_server_onBeforeShutdown(Server *serv); static void php_swoole_server_onShutdown(Server *); -static void php_swoole_server_onWorkerStart(Server *, int worker_id); +static void php_swoole_server_onWorkerStart(Server *, Worker *worker); static void php_swoole_server_onBeforeReload(Server *serv); static void php_swoole_server_onAfterReload(Server *serv); -static void php_swoole_server_onWorkerStop(Server *, int worker_id); -static void php_swoole_server_onWorkerExit(Server *serv, int worker_id); +static void php_swoole_server_onWorkerStop(Server *, Worker *worker); +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker); static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker); static int php_swoole_server_onTask(Server *, EventData *task); static int php_swoole_server_onFinish(Server *, EventData *task); static void php_swoole_server_onPipeMessage(Server *serv, EventData *req); -static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const ExitStatus &exit_status); +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status); static void php_swoole_server_onManagerStart(Server *serv); static void php_swoole_server_onManagerStop(Server *serv); -static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_task); +static bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task); +static TaskId php_swoole_server_task_pack(zval *data, EventData *task); +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result); static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data); static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port); void php_swoole_server_rshutdown() { - if (!sw_server()) { + if (!sw_server() || !sw_worker()) { return; } Server *serv = sw_server(); + Worker *worker = sw_worker(); serv->drain_worker_pipe(); - if (serv->is_started() && !serv->is_user_worker()) { + if (serv->is_started() && worker->is_running() && !serv->is_user_worker()) { + worker->shutdown(); + if (serv->is_event_worker()) { + serv->clean_worker_connections(worker); + } if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, "Fatal error: %s in %s on line %d", -#if PHP_VERSION_ID < 80000 - PG(last_error_message), -#else - PG(last_error_message)->val, -#endif - -#if PHP_VERSION_ID >= 80100 - PG(last_error_file) ? PG(last_error_file)->val : "-", -#else - PG(last_error_file) ? PG(last_error_file) : "-", -#endif + php_swoole_get_last_error_message(), + php_swoole_get_last_error_file(), PG(last_error_lineno)); } else { swoole_error_log( @@ -138,6 +143,12 @@ static zend_object_handlers swoole_server_status_info_handlers; static zend_class_entry *swoole_server_task_result_ce; static zend_object_handlers swoole_server_task_result_handlers; +static SW_THREAD_LOCAL zval swoole_server_instance; +#ifdef SW_THREAD +static SW_THREAD_LOCAL WorkerFn worker_thread_fn; +static SW_THREAD_LOCAL std::vector swoole_server_port_properties; +#endif + static sw_inline ServerObject *server_fetch_object(zend_object *obj) { return (ServerObject *) ((char *) obj - swoole_server_handlers.offset); } @@ -154,16 +165,35 @@ Server *php_swoole_server_get_and_check_server(zval *zobject) { return serv; } -zval *php_swoole_server_get_zval_object(Server *serv) { - return (zval *) serv->private_data_2; +zval *php_swoole_server_zval_ptr(Server *serv) { + return &swoole_server_instance; +} + +ServerPortProperty *php_swoole_server_get_port_property(ListenPort *port) { +#ifdef SW_THREAD + return swoole_server_port_properties.at(port->socket->get_fd()); +#else + return (ServerPortProperty *) port->ptr; +#endif +} + +void php_swoole_server_set_port_property(ListenPort *port, ServerPortProperty *property) { +#ifdef SW_THREAD + if (swoole_server_port_properties.size() < (size_t) port->socket->get_fd() + 1) { + swoole_server_port_properties.resize((size_t) port->socket->get_fd() + 1); + } + swoole_server_port_properties[port->socket->get_fd()] = property; +#else + port->ptr = property; +#endif } ServerObject *php_swoole_server_get_zend_object(Server *serv) { - return server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + return server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); } bool php_swoole_server_isset_callback(Server *serv, ListenPort *port, int event_type) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); return server_object->isset_callback(port, event_type); } @@ -178,16 +208,12 @@ static void server_free_object(zend_object *object) { if (serv) { if (serv->private_data_3) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); - efree(serv->private_data_3); - } - if (serv->private_data_2) { - efree(serv->private_data_2); + sw_callable_free(serv->private_data_3); } for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { - zend_fcall_info_cache *fci_cache = property->callbacks[i]; + auto fci_cache = property->callbacks[i]; if (fci_cache) { - efree(fci_cache); + sw_callable_free(fci_cache); property->callbacks[i] = nullptr; } } @@ -202,14 +228,18 @@ static void server_free_object(zend_object *object) { } for (auto fci_cache : property->command_callbacks) { - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + sw_callable_free(fci_cache); } delete property; zend_object_std_dtor(object); if (serv && serv->is_master()) { +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + zend_string_release((zend_string *) serv->private_data_4); + } +#endif delete serv; } } @@ -303,197 +333,6 @@ static zend_object *php_swoole_server_task_create_object(zend_class_entry *ce) { return &server_task->std; } -// arginfo server -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server__construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, mode) - ZEND_ARG_INFO(0, sock_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_send, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, send_data) - ZEND_ARG_INFO(0, server_socket) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendwait, 0, 0, 2) - ZEND_ARG_INFO(0, conn_fd) - ZEND_ARG_INFO(0, send_data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_exists, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_protect, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, is_protected) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, ip) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, send_data) - ZEND_ARG_INFO(0, server_socket) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendfile, 0, 0, 2) - ZEND_ARG_INFO(0, conn_fd) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_close, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, reset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_pause, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_resume, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -#ifdef SWOOLE_SOCKETS_SUPPORT -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getSocket, 0, 0, 0) - ZEND_ARG_INFO(0, port) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getCallback, 0, 0, 1) - ZEND_ARG_INFO(0, event_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_listen, 0, 0, 3) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, sock_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_task, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, worker_id) - ZEND_ARG_CALLABLE_INFO(0, finish_callback, 1) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskwait, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskCo, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, tasks, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskWaitMulti, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, tasks, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_finish, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_task_pack, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_reload, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_heartbeat, 0, 0, 1) - ZEND_ARG_INFO(0, reactor_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_stop, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_bind, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, uid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendMessage, 0, 0, 2) - ZEND_ARG_INFO(0, message) - ZEND_ARG_INFO(0, dst_worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_command, 0, 0, 4) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, process_id) - ZEND_ARG_INFO(0, process_type) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, json_encode) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addProcess, 0, 0, 1) - ZEND_ARG_OBJ_INFO(0, process, swoole_process, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addCommand, 0, 0, 3) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, accepted_process_types) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getClientInfo, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, reactor_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getWorkerStatus, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getWorkerPid, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getClientList, 0, 0, 1) - ZEND_ARG_INFO(0, start_fd) - ZEND_ARG_INFO(0, find_count) -ZEND_END_ARG_INFO() - -//arginfo connection_iterator -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetExists, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetGet, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetUnset, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetSet, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() -//arginfo end -// clang-format on - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_server, __construct); static PHP_METHOD(swoole_server, __destruct); @@ -558,105 +397,102 @@ static PHP_METHOD(swoole_connection_iterator, __destruct); */ static PHP_METHOD(swoole_server_task, finish); static PHP_METHOD(swoole_server_task, pack); +static PHP_METHOD(swoole_server_task, unpack); SW_EXTERN_C_END // clang-format off static zend_function_entry swoole_server_methods[] = { - PHP_ME(swoole_server, __construct, arginfo_swoole_server__construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, listen, arginfo_swoole_server_listen, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, addlistener, listen, arginfo_swoole_server_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, on, arginfo_swoole_server_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getCallback, arginfo_swoole_server_getCallback, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, set, arginfo_swoole_server_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, send, arginfo_swoole_server_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendto, arginfo_swoole_server_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendwait, arginfo_swoole_server_sendwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, exists, arginfo_swoole_server_exists, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, exist, exists, arginfo_swoole_server_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, protect, arginfo_swoole_server_protect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendfile, arginfo_swoole_server_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, close, arginfo_swoole_server_close, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, confirm, resume, arginfo_swoole_server_resume, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, pause, arginfo_swoole_server_pause, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, resume, arginfo_swoole_server_resume, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, task, arginfo_swoole_server_task, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskwait, arginfo_swoole_server_taskwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskWaitMulti, arginfo_swoole_server_taskWaitMulti, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskCo, arginfo_swoole_server_taskCo, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, finish, arginfo_swoole_server_finish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, reload, arginfo_swoole_server_reload, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, shutdown, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, stop, arginfo_swoole_server_stop, ZEND_ACC_PUBLIC) - PHP_FALIAS(getLastError, swoole_last_error, arginfo_swoole_void) - PHP_ME(swoole_server, heartbeat, arginfo_swoole_server_heartbeat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getClientInfo, arginfo_swoole_server_getClientInfo, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getClientList, arginfo_swoole_server_getClientList, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerId, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerPid, arginfo_swoole_server_getWorkerPid, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerStatus, arginfo_swoole_server_getWorkerStatus, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getManagerPid, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getMasterPid, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, __construct, arginfo_class_Swoole_Server___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, __destruct, arginfo_class_Swoole_Server___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, addlistener, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, on, arginfo_class_Swoole_Server_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getCallback, arginfo_class_Swoole_Server_getCallback, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, set, arginfo_class_Swoole_Server_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, start, arginfo_class_Swoole_Server_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, send, arginfo_class_Swoole_Server_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendto, arginfo_class_Swoole_Server_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendwait, arginfo_class_Swoole_Server_sendwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, exist, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, protect, arginfo_class_Swoole_Server_protect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendfile, arginfo_class_Swoole_Server_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, close, arginfo_class_Swoole_Server_close, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, confirm, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, pause, arginfo_class_Swoole_Server_pause, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, task, arginfo_class_Swoole_Server_task, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskwait, arginfo_class_Swoole_Server_taskwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskWaitMulti, arginfo_class_Swoole_Server_taskWaitMulti, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskCo, arginfo_class_Swoole_Server_taskCo, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, finish, arginfo_class_Swoole_Server_finish, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, reload, arginfo_class_Swoole_Server_reload, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, shutdown, arginfo_class_Swoole_Server_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, stop, arginfo_class_Swoole_Server_stop, ZEND_ACC_PUBLIC) + PHP_FALIAS(getLastError, swoole_last_error, arginfo_class_Swoole_Server_getLastError) + PHP_ME(swoole_server, heartbeat, arginfo_class_Swoole_Server_heartbeat, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerId, arginfo_class_Swoole_Server_getWorkerId, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerPid, arginfo_class_Swoole_Server_getWorkerPid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerStatus, arginfo_class_Swoole_Server_getWorkerStatus, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getManagerPid, arginfo_class_Swoole_Server_getManagerPid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getMasterPid, arginfo_class_Swoole_Server_getMasterPid, ZEND_ACC_PUBLIC) //psr-0 style - PHP_MALIAS(swoole_server, connection_info, getClientInfo, arginfo_swoole_server_getClientInfo, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, connection_list, getClientList, arginfo_swoole_server_getClientList, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, connection_info, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, connection_list, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC) //process - PHP_ME(swoole_server, sendMessage, arginfo_swoole_server_sendMessage, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, command, arginfo_swoole_server_command, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, addCommand, arginfo_swoole_server_addCommand, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, addProcess, arginfo_swoole_server_addProcess, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, stats, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendMessage, arginfo_class_Swoole_Server_sendMessage, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, command, arginfo_class_Swoole_Server_command, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, addCommand, arginfo_class_Swoole_Server_addCommand, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, addProcess, arginfo_class_Swoole_Server_addProcess, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, stats, arginfo_class_Swoole_Server_stats, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server, getSocket, arginfo_swoole_server_getSocket, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_server, bind, arginfo_swoole_server_bind, ZEND_ACC_PUBLIC) - {nullptr, nullptr, nullptr} + PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) + PHP_FE_END }; static const zend_function_entry swoole_connection_iterator_methods[] = { - PHP_ME(swoole_connection_iterator, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, rewind, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, next, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, current, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, key, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, valid, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, count, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetExists, arginfo_swoole_connection_iterator_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetGet, arginfo_swoole_connection_iterator_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetSet, arginfo_swoole_connection_iterator_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetUnset, arginfo_swoole_connection_iterator_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, __construct, arginfo_class_Swoole_Connection_Iterator___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, __destruct, arginfo_class_Swoole_Connection_Iterator___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, rewind, arginfo_class_Swoole_Connection_Iterator_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, next, arginfo_class_Swoole_Connection_Iterator_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, current, arginfo_class_Swoole_Connection_Iterator_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, key, arginfo_class_Swoole_Connection_Iterator_key, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, valid, arginfo_class_Swoole_Connection_Iterator_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, count, arginfo_class_Swoole_Connection_Iterator_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetExists, arginfo_class_Swoole_Connection_Iterator_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetGet, arginfo_class_Swoole_Connection_Iterator_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetSet, arginfo_class_Swoole_Connection_Iterator_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetUnset, arginfo_class_Swoole_Connection_Iterator_offsetUnset, ZEND_ACC_PUBLIC) PHP_FE_END }; static const zend_function_entry swoole_server_task_methods[] = { - PHP_ME(swoole_server_task, finish, arginfo_swoole_server_finish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_task, pack, arginfo_swoole_server_task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_server_task, finish, arginfo_class_Swoole_Server_Task_finish, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_task, pack, arginfo_class_Swoole_Server_Task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_server_task, unpack, arginfo_class_Swoole_Server_Task_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on +#define SWOOLE_DISPATCH_STREAM 7 // Deprecated, kept for forward compatibility + void php_swoole_server_minit(int module_number) { // ---------------------------------------Server------------------------------------- - SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", "swoole_server", nullptr, swoole_server_methods); + SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", nullptr, swoole_server_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server); SW_SET_CLASS_CLONEABLE(swoole_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server, server_create_object, server_free_object, ServerObject, std); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "after", &swoole_server_ce->function_table, "after"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "tick", &swoole_server_ce->function_table, "tick"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clear", &swoole_server_ce->function_table, "clearTimer"); - - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", &swoole_server_ce->function_table, "defer"); - // ---------------------------------------Task------------------------------------- - SW_INIT_CLASS_ENTRY( - swoole_server_task, "Swoole\\Server\\Task", "swoole_server_task", nullptr, swoole_server_task_methods); + SW_INIT_CLASS_ENTRY(swoole_server_task, "Swoole\\Server\\Task", nullptr, swoole_server_task_methods); swoole_server_task_ce->ce_flags |= ZEND_ACC_FINAL; SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_task); SW_SET_CLASS_CLONEABLE(swoole_server_task, sw_zend_class_clone_deny); @@ -687,6 +523,7 @@ void php_swoole_server_minit(int module_number) { // ---------------------------------------PipeMessage------------------------------------- SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_pipe_message, "Swoole\\Server\\PipeMessage"); zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL("source_worker_id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL("worker_id"), 0, ZEND_ACC_PUBLIC); zend_declare_property_double(swoole_server_pipe_message_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_pipe_message_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); // ---------------------------------------StatusInfo------------------------------------- @@ -703,11 +540,8 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_double(swoole_server_task_result_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_task_result_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); // ---------------------------------------Connection Iterator------------------------------------- - SW_INIT_CLASS_ENTRY(swoole_connection_iterator, - "Swoole\\Connection\\Iterator", - "swoole_connection_iterator", - nullptr, - swoole_connection_iterator_methods); + SW_INIT_CLASS_ENTRY( + swoole_connection_iterator, "Swoole\\Connection\\Iterator", nullptr, swoole_connection_iterator_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_connection_iterator); SW_SET_CLASS_CLONEABLE(swoole_connection_iterator, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_connection_iterator, sw_zend_class_unset_property_deny); @@ -741,6 +575,7 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_string(swoole_server_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_server_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("mode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("ports"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("master_pid"), 0, ZEND_ACC_PUBLIC); @@ -750,13 +585,18 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_long(swoole_server_ce, ZEND_STRL("worker_pid"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("stats_timer"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("admin_server"), ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_string(swoole_server_ce, ZEND_STRL("bootstrap"), "", ZEND_ACC_PUBLIC); +#endif /** * mode type */ SW_REGISTER_LONG_CONSTANT("SWOOLE_BASE", swoole::Server::MODE_BASE); SW_REGISTER_LONG_CONSTANT("SWOOLE_PROCESS", swoole::Server::MODE_PROCESS); - +#ifdef SW_THREAD + SW_REGISTER_LONG_CONSTANT("SWOOLE_THREAD", swoole::Server::MODE_THREAD); +#endif /** * task ipc mode */ @@ -771,6 +611,17 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_WORKER", Server::Command::EVENT_WORKER); SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_TASK_WORKER", Server::Command::TASK_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_ROUND", Server::DISPATCH_ROUND); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_FDMOD", Server::DISPATCH_FDMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_IDLE_WORKER", Server::DISPATCH_IDLE_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_IPMOD", Server::DISPATCH_IPMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_UIDMOD", Server::DISPATCH_UIDMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_USERFUNC", Server::DISPATCH_USERFUNC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_STREAM", SWOOLE_DISPATCH_STREAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_CONN_LB", Server::DISPATCH_CO_CONN_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_REQ_LB", Server::DISPATCH_CO_REQ_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CONCURRENT_LB", Server::DISPATCH_CONCURRENT_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_DISCARD_PACKET", Server::DISPATCH_RESULT_DISCARD_PACKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_CLOSE_CONNECTION", Server::DISPATCH_RESULT_CLOSE_CONNECTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_USERFUNC_FALLBACK", Server::DISPATCH_RESULT_USERFUNC_FALLBACK); @@ -789,19 +640,18 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_EXIT", SW_WORKER_EXIT); } -zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_fd, int event_type) { +zend::Callable *php_swoole_server_get_callback(Server *serv, int server_fd, int event_type) { ListenPort *port = serv->get_port_by_server_fd(server_fd); - ServerPortProperty *property; - zend_fcall_info_cache *fci_cache; - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerPortProperty *property = php_swoole_server_get_port_property(port); + zend::Callable *cb; if (sw_unlikely(!port)) { return nullptr; } - if ((property = (ServerPortProperty *) port->ptr) && (fci_cache = property->caches[event_type])) { - return fci_cache; + if (property && (cb = property->callbacks[event_type])) { + return cb; } else { - return server_object->property->primary_port->caches[event_type]; + return php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]; } } @@ -812,7 +662,7 @@ int php_swoole_create_dir(const char *path, size_t length) { return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, nullptr) ? 0 : -1; } -TaskId php_swoole_task_pack(EventData *task, zval *zdata) { +static TaskId php_swoole_server_task_pack(zval *zdata, EventData *task) { smart_str serialized_data = {}; php_serialize_data_t var_hash; @@ -860,7 +710,7 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { } else { if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { zend::assign_zend_string_by_val(zdata, (char *) data, length); - serv->message_bus.move_packet(); + serv->get_worker_message_bus()->move_packet(); } else if (req->info.flags & SW_EVENT_DATA_POP_PTR) { String *recv_buffer = serv->get_recv_buffer(serv->get_connection_by_session_id(req->info.fd)->socket); zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length); @@ -870,7 +720,7 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { } } -static sw_inline int php_swoole_check_task_param(Server *serv, zend_long dst_worker_id) { +static inline int php_swoole_server_task_check_param(Server *serv, zend_long dst_worker_id) { if (UNEXPECTED(serv->task_worker_num == 0)) { php_swoole_fatal_error(E_WARNING, "task method can't be executed without task worker"); return SW_ERR; @@ -886,40 +736,31 @@ static sw_inline int php_swoole_check_task_param(Server *serv, zend_long dst_wor return SW_OK; } -zval *php_swoole_task_unpack(EventData *task_result) { - zval *result_data, *result_unserialized_data; +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) { php_unserialize_data_t var_hash; PacketPtr packet; if (!Server::task_unpack(task_result, sw_tg_buffer(), &packet)) { - return nullptr; + return false; } - char *result_data_str = packet.data; - size_t result_data_len = packet.length; - if (task_result->info.ext_flags & SW_TASK_SERIALIZE) { - result_unserialized_data = sw_malloc_zval(); - + char *p = packet.data; + size_t l = packet.length; PHP_VAR_UNSERIALIZE_INIT(var_hash); - // unserialize success - if (php_var_unserialize(result_unserialized_data, - (const unsigned char **) &result_data_str, - (const unsigned char *) (result_data_str + result_data_len), - &var_hash)) { - result_data = result_unserialized_data; - } - // failed - else { - result_data = sw_malloc_zval(); - ZVAL_STRINGL(result_data, result_data_str, result_data_len); - } + zend_bool unserialized = php_var_unserialize(zresult, (const uchar **) &p, (const uchar *) (p + l), &var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - packet.data), + l); + return false; + } } else { - result_data = sw_malloc_zval(); - ZVAL_STRINGL(result_data, result_data_str, result_data_len); + ZVAL_STRINGL(zresult, packet.data, packet.length); } - return result_data; + + return true; } extern ListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject); @@ -927,11 +768,9 @@ extern void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port); extern ServerPortProperty *php_swoole_server_port_get_property(zval *zobject); static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port) { - /* port */ - zval *zport; Server *serv = server_object->serv; - zport = sw_malloc_zval(); + zval *zport = sw_malloc_zval(); object_init_ex(zport, swoole_server_port_ce); server_object->property->ports.push_back(zport); @@ -944,15 +783,16 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort property->port = port; /* linked */ - port->ptr = property; + php_swoole_server_set_port_property(port, property); zend_update_property_string(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("host"), port->get_host()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->get_port()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("type"), port->get_type()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("sock"), port->get_fd()); + zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); do { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval *zports = sw_zend_read_and_convert_property_array(Z_OBJCE_P(zserv), zserv, ZEND_STRL("ports"), 0); (void) add_next_index_zval(zports, zport); } while (0); @@ -973,6 +813,17 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort return zport; } +void ServerObject::copy_setting(zval *zsetting) { + zend_array *new_array = zend_array_dup(Z_ARRVAL_P(zsetting)); + zend_hash_apply(new_array, [](zval *el) -> int { + return sw_zval_is_serializable(el) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; + }); + zval znew_array; + ZVAL_ARR(&znew_array, new_array); + serv->private_data_4 = php_swoole_serialize(&znew_array); + zval_ptr_dtor(&znew_array); +} + void ServerObject::on_before_start() { /** * create swoole server @@ -982,7 +833,7 @@ void ServerObject::on_before_start() { return; } - zval *zobject = get_object(); + zval *zobject = php_swoole_server_zval_ptr(serv); auto primary_port = serv->get_primary_port(); #ifdef SW_LOG_TRACE_OPEN @@ -1006,7 +857,7 @@ void ServerObject::on_before_start() { serv->message_bus.set_allocator(sw_zend_string_allocator()); - if (serv->is_base_mode()) { + if (serv->is_base_mode() || serv->is_thread_mode()) { serv->recv_buffer_allocator = sw_zend_string_allocator(); } @@ -1029,6 +880,10 @@ void ServerObject::on_before_start() { if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("max_connection"))) { add_assoc_long(zsetting, "max_connection", serv->get_max_connection()); } + // for admin_server + if (zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("admin_server"))) { + swoole::register_admin_server_commands(serv); + } bool find_http_port = false; if (is_redis_server()) { @@ -1151,9 +1006,17 @@ void ServerObject::on_before_start() { if (find_http_port) { serv->onReceive = php_swoole_http_server_onReceive; - php_swoole_http_server_init_global_variant(); + if (serv->is_base_mode()) { + serv->onClose = php_swoole_http_server_onClose; + } } +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); + } +#endif + if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); } @@ -1186,13 +1049,12 @@ void ServerObject::register_callback() { } } -static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_task) { +static bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task) { int flags = 0; smart_str serialized_data = {}; php_serialize_data_t var_hash; char *data_str; size_t data_len = 0; - int ret; // need serialize if (Z_TYPE_P(zdata) != IS_STRING) { @@ -1210,18 +1072,18 @@ static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_ data_len = Z_STRLEN_P(zdata); } - ret = serv->reply_task_result(data_str, data_len, flags, current_task); + bool success = serv->finish(data_str, data_len, flags, current_task); smart_str_free(&serialized_data); - return ret; + return success; } static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); - zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; - zval *zserv = (zval *) serv->private_data_2; + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + auto cb = server_object->get_callback(SW_SERVER_CB_onPipeMessage); + zval *zserv = php_swoole_server_zval_ptr(serv); - zval *zdata = php_swoole_task_unpack(req); - if (UNEXPECTED(zdata == nullptr)) { + zend::Variable zresult; + if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { return; } @@ -1243,33 +1105,35 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { zend_update_property_long(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), - (zend_long) req->info.reactor_id); + (zend_long) serv->get_task_src_worker_id(req)); + zend_update_property_long(swoole_server_pipe_message_ce, + SW_Z8_OBJ_P(object), + ZEND_STRL("source_worker_id"), + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_double( swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zdata); + zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { - ZVAL_LONG(&args[1], (zend_long) req->info.reactor_id); - args[2] = *zdata; + ZVAL_LONG(&args[1], (zend_long) serv->get_task_src_worker_id(req)); + args[2] = zresult.value; argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } if (serv->event_object) { zval_ptr_dtor(&args[1]); } - - sw_zval_free(zdata); } int php_swoole_server_onReceive(Server *serv, RecvData *req) { - auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onReceive); - if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + if (cb) { + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[4]; int argc; @@ -1296,7 +1160,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { argc = 4; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); serv->close(req->info.fd, false); } @@ -1311,7 +1175,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { } int php_swoole_server_onPacket(Server *serv, RecvData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; @@ -1386,8 +1250,8 @@ int php_swoole_server_onPacket(Server *serv, RecvData *req) { argc = 3; } - auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onPacket); - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onPacket); + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1404,9 +1268,12 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E php_swoole_server_task_set_server(ztask, serv); php_swoole_server_task_set_info(ztask, &req->info); + zend_update_property_long(swoole_server_task_ce, + SW_Z8_OBJ_P(ztask), + ZEND_STRL("worker_id"), + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_long( - swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("worker_id"), (zend_long) req->info.reactor_id); - zend_update_property_long(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("id"), (zend_long) req->info.fd); + swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("id"), (zend_long) serv->get_task_id(req)); zend_update_property(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("data"), zdata); zend_update_property_double(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("dispatch_time"), req->info.time); zend_update_property_long( @@ -1416,11 +1283,11 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E static int php_swoole_server_onTask(Server *serv, EventData *req) { sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zval *zdata = php_swoole_task_unpack(req); - if (zdata == nullptr) { + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { return SW_ERR; } @@ -1431,16 +1298,16 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { if (serv->task_enable_coroutine || serv->task_object) { argc = 2; argv[0] = *zserv; - php_swoole_create_task_object(&argv[1], serv, req, zdata); + php_swoole_create_task_object(&argv[1], serv, req, zresult.ptr()); } else { argc = 4; argv[0] = *zserv; - ZVAL_LONG(&argv[1], (zend_long) req->info.fd); - ZVAL_LONG(&argv[2], (zend_long) req->info.reactor_id); - argv[3] = *zdata; + ZVAL_LONG(&argv[1], (zend_long) serv->get_task_id(req)); + ZVAL_LONG(&argv[2], (zend_long) serv->get_task_src_worker_id(req)); + argv[3] = zresult.value; } - if (UNEXPECTED(!zend::function::call(server_object->property->callbacks[SW_SERVER_CB_onTask], + if (UNEXPECTED(!zend::function::call(server_object->get_callback(SW_SERVER_CB_onTask)->ptr(), argc, argv, &retval, @@ -1451,10 +1318,9 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { if (argc == 2) { zval_ptr_dtor(&argv[1]); } - sw_zval_free(zdata); if (!ZVAL_IS_NULL(&retval)) { - php_swoole_task_finish(serv, &retval, req); + php_swoole_server_task_finish(serv, &retval, req); zval_ptr_dtor(&retval); } @@ -1462,36 +1328,32 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { } static int php_swoole_server_onFinish(Server *serv, EventData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + TaskId task_id = serv->get_task_id(req); - zval *zdata = php_swoole_task_unpack(req); - if (zdata == nullptr) { + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { return SW_ERR; } if (req->info.ext_flags & SW_TASK_COROUTINE) { - TaskId task_id = req->info.fd; auto task_co_iterator = server_object->property->task_coroutine_map.find(task_id); - if (task_co_iterator == server_object->property->task_coroutine_map.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%ld] has expired", task_id); - _fail: - sw_zval_free(zdata); return SW_OK; } TaskCo *task_co = task_co_iterator->second; // Server->taskwait if (task_co->list == nullptr) { - ZVAL_COPY_VALUE(task_co->result, zdata); + ZVAL_COPY_VALUE(task_co->result, zresult.ptr()); + zresult.reset(); task_co->co->resume(); return SW_OK; } // Server->taskCo - uint32_t i; int task_index = -1; - zval *result = task_co->result; - for (i = 0; i < task_co->count; i++) { + SW_LOOP_N(task_co->count) { if (task_co->list[i] == task_id) { task_index = i; break; @@ -1499,32 +1361,32 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (task_index < 0) { php_swoole_fatal_error(E_WARNING, "task[%ld] is invalid", task_id); - goto _fail; + return SW_OK; } - (void) add_index_zval(result, task_index, zdata); - efree(zdata); + (void) add_index_zval(task_co->result, task_index, zresult.ptr()); + zresult.reset(); server_object->property->task_coroutine_map.erase(task_id); - if (php_swoole_array_length(result) == task_co->count) { + if (php_swoole_array_length(task_co->result) == task_co->count) { task_co->co->resume(); } return SW_OK; } - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *cb = nullptr; if (req->info.ext_flags & SW_TASK_CALLBACK) { - auto callback_iterator = server_object->property->task_callbacks.find(req->info.fd); + auto callback_iterator = server_object->property->task_callbacks.find(task_id); if (callback_iterator == server_object->property->task_callbacks.end()) { req->info.ext_flags = req->info.ext_flags & (~SW_TASK_CALLBACK); } else { - fci_cache = &callback_iterator->second; + cb = callback_iterator->second; } } else { - fci_cache = server_object->property->callbacks[SW_SERVER_CB_onFinish]; + cb = server_object->get_callback(SW_SERVER_CB_onFinish); } - if (UNEXPECTED(fci_cache == nullptr)) { - sw_zval_free(zdata); - php_swoole_fatal_error(E_WARNING, "require onFinish callback"); + + if (UNEXPECTED(cb == nullptr)) { + php_swoole_fatal_error(E_WARNING, "require 'onFinish' callback"); return SW_ERR; } @@ -1536,29 +1398,28 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval *object = &args[1]; object_init_ex(object, swoole_server_task_result_ce); zend_update_property_long( - swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_id"), (zend_long) req->info.fd); + swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_id"), (zend_long) task_id); zend_update_property_long(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_worker_id"), - (zend_long) req->info.reactor_id); + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_double( swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zdata); + zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { - ZVAL_LONG(&args[1], req->info.fd); - args[2] = *zdata; + ZVAL_LONG(&args[1], (zend_long) task_id); + args[2] = zresult.value; argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onFinish handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } if (req->info.ext_flags & SW_TASK_CALLBACK) { - sw_zend_fci_cache_discard(fci_cache); - server_object->property->task_callbacks.erase(req->info.fd); + sw_callable_free(server_object->property->task_callbacks[task_id]); + server_object->property->task_callbacks.erase(task_id); } - sw_zval_free(zdata); if (serv->event_object) { zval_ptr_dtor(&args[1]); } @@ -1567,10 +1428,9 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } static void php_swoole_server_onStart(Server *serv) { - serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1579,16 +1439,15 @@ static void php_swoole_server_onStart(Server *serv) { zend::function::call("\\Swoole\\Server\\Helper::onStart", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } static void php_swoole_server_onManagerStart(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1597,143 +1456,140 @@ static void php_swoole_server_onManagerStart(Server *serv) { zend::function::call("\\Swoole\\Server\\Helper::onManagerStart", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onManagerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } static void php_swoole_server_onManagerStop(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; + auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStop); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onManagerStop", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onManagerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } static void php_swoole_server_onBeforeShutdown(Server *serv) { - serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeShutdown]; + auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeShutdown); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeShutdown", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onBeforeShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } static void php_swoole_server_onShutdown(Server *serv) { - serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onShutdown]; + auto cb = server_object->get_callback(SW_SERVER_CB_onShutdown); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onShutdown", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } -static void php_swoole_server_onWorkerStart(Server *serv, int worker_id) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_id"), worker_id); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_id"), worker->id); zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("taskworker"), serv->is_task_worker()); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_pid"), getpid()); if (serv->is_task_worker() && !serv->task_enable_coroutine) { PHPCoroutine::disable_hook(); } + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStart", 2, args); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } static void php_swoole_server_onBeforeReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeReload]; + auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeReload); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeReload", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBeforeReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } static void php_swoole_server_onAfterReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onAfterReload]; + auto cb = server_object->get_callback(SW_SERVER_CB_onAfterReload); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onAfterReload", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onAfterReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } -static void php_swoole_server_onWorkerStop(Server *serv, int worker_id) { - if (SwooleWG.shutdown) { +static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { + if (!worker->is_running()) { return; } - SwooleWG.shutdown = true; - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; + auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStop); + zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStop", 2, args); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } -static void php_swoole_server_onWorkerExit(Server *serv, int worker_id) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerExit); zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerExit", 2, args); @@ -1745,20 +1601,29 @@ static void php_swoole_server_onWorkerExit(Server *serv, int worker_id) { } static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { - zval *object = (zval *) worker->ptr; - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), SwooleG.process_id); + zval *object; + zval *zserv = php_swoole_server_zval_ptr(serv); + + if (serv->is_thread_mode()) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + int index = worker->id - serv->worker_num - serv->task_worker_num; + object = server_object->property->user_processes[index]; + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + } else { + object = (zval *) worker->ptr; + } - zval *zserv = (zval *) serv->private_data_2; + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); php_swoole_process_start(worker, object); } -static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const ExitStatus &exit_status) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerError); zval args[5]; int argc; @@ -1767,7 +1632,8 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E if (serv->event_object) { zval *object = &args[1]; object_init_ex(object, swoole_server_status_info_ce); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker_id); + zend_update_property_long( + swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker->id); zend_update_property_long( swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_pid"), exit_status.get_pid()); zend_update_property_long( @@ -1778,7 +1644,7 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("signal"), exit_status.get_signal()); argc = 2; } else { - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); ZVAL_LONG(&args[2], exit_status.get_pid()); ZVAL_LONG(&args[3], exit_status.get_code()); ZVAL_LONG(&args[4], exit_status.get_signal()); @@ -1799,12 +1665,12 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E } void php_swoole_server_onConnect(Server *serv, DataHead *info) { - auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onConnect); - if (!fci_cache) { + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onConnect); + if (!cb) { return; } - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1824,7 +1690,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1834,7 +1700,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { } void php_swoole_server_onClose(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); SessionId session_id = info->fd; @@ -1853,7 +1719,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } } - auto *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onClose); + auto *cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onClose); Connection *conn = serv->get_connection_by_session_id(session_id); if (!conn) { return; @@ -1862,11 +1728,11 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { ListenPort *port = serv->get_port_by_server_fd(info->server_fd); if (port && port->open_websocket_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onDisconnect)) { - fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onDisconnect); + cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onDisconnect); } } - if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + if (cb) { + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1887,7 +1753,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onClose handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1895,32 +1761,29 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { zval_ptr_dtor(&args[1]); } } - -#ifdef SW_USE_HTTP2 if (conn->http2_stream) { swoole_http2_server_session_free(conn); } -#endif } void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; - auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); + zval *zserv = php_swoole_server_zval_ptr(serv); + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferFull); - if (fci_cache) { + if (cb) { zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], info->fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBufferFull handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } } void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdata, zval *return_value) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); Coroutine *co = Coroutine::get_current_safe(); char *data; size_t length = php_swoole_get_send_data(zdata, &data); @@ -1954,23 +1817,22 @@ void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdat } static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data) { - serv->lock(); - - auto fci_cache = (zend_fcall_info_cache *) serv->private_data_3; + auto cb = (zend::Callable *) serv->private_data_3; zval args[4]; zval *zserv = &args[0], *zfd = &args[1], *ztype = &args[2], *zdata = nullptr; zval retval; zend_long worker_id = -1; - *zserv = *((zval *) serv->private_data_2); + *zserv = *(php_swoole_server_zval_ptr(serv)); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); - ZVAL_LONG(ztype, (zend_long)(data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); - if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { + ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); + if (data && sw_zend_function_max_num_args(cb->ptr()->function_handler) > 3) { // TODO: reduce memory copy zdata = &args[3]; ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len); } - if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval) != SUCCESS)) { + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), zdata ? 4 : 3, args, &retval);); + if (UNEXPECTED(call_result != SUCCESS)) { php_swoole_error(E_WARNING, "%s->onDispatch handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } else if (!ZVAL_IS_NULL(&retval)) { worker_id = zval_get_long(&retval); @@ -1984,8 +1846,6 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zval_ptr_dtor(zdata); } - serv->unlock(); - /* the exception should only be thrown after unlocked */ if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); @@ -1995,7 +1855,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD } void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); if (serv->send_yield) { @@ -2012,25 +1872,57 @@ void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { } } - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty); - if (fci_cache) { + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty); + if (cb) { zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], info->fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBufferEmpty handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } } +static void server_ctor(zval *zserv, Server *serv) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + *php_swoole_server_zval_ptr(serv) = *zserv; + server_set_ptr(zserv, serv); + + /* primary port */ + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); + } + + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); + + ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); + iterator->serv = serv; + + zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); + + /* info */ + auto port = serv->get_primary_port(); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv->get_mode()); + zend_update_property_stringl( + swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), port->host.c_str(), port->host.length()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); +} + static PHP_METHOD(swoole_server, __construct) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = server_object->serv; if (serv) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zval *zserv = ZEND_THIS; @@ -2038,21 +1930,14 @@ static PHP_METHOD(swoole_server, __construct) { size_t host_len = 0; zend_long sock_type = SW_SOCK_TCP; zend_long serv_port = 0; - zend_long serv_mode = Server::MODE_PROCESS; + zend_long serv_mode = Server::MODE_BASE; - // only cli env if (!SWOOLE_G(cli)) { zend_throw_exception_ex( swoole_exception_ce, -1, "%s can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; } - if (sw_server() != nullptr) { - zend_throw_exception_ex( - swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_STRING(host, host_len) Z_PARAM_OPTIONAL @@ -2061,66 +1946,62 @@ static PHP_METHOD(swoole_server, __construct) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { - php_swoole_fatal_error(E_ERROR, "invalid $mode parameters %d", (int) serv_mode); + if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS +#ifdef SW_THREAD + && serv_mode != Server::MODE_THREAD +#endif + ) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } +#ifdef SW_THREAD + if (sw_server() && sw_server()->is_worker_thread()) { + server_ctor(ZEND_THIS, sw_server()); + return; + } + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_exception_ex(swoole_exception_ce, -2, "This operation is only allowed in the main thread"); + RETURN_FALSE; + } +#else + if (sw_server() != nullptr) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_exception_ex( + swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); + RETURN_FALSE; + } +#endif + serv = new Server((enum Server::Mode) serv_mode); - serv->private_data_2 = sw_zval_dup(zserv); - server_set_ptr(zserv, serv); if (serv_mode == Server::MODE_BASE) { serv->reactor_num = 1; serv->worker_num = 1; } - /* primary port */ - do { - if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { - if (serv->add_systemd_socket() <= 0) { - php_swoole_fatal_error(E_ERROR, "failed to add systemd socket"); - RETURN_FALSE; - } - } else { - ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); - if (!port) { - zend_throw_exception_ex(swoole_exception_ce, - errno, - "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", - host, - serv_port, - strerror(errno), - errno); - RETURN_FALSE; - } + if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { + if (serv->add_systemd_socket() <= 0) { + zend_throw_error(NULL, "failed to add systemd socket"); + RETURN_FALSE; } - - for (auto ls : serv->ports) { - php_swoole_server_add_port(server_object, ls); + } else { + ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); + if (!port) { + zend_throw_exception_ex(swoole_exception_ce, + swoole_get_last_error(), + "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", + host, + serv_port, + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); + RETURN_FALSE; } + } - server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr; - } while (0); - - /* iterator */ - do { - zval connection_iterator; - object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - - ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); - iterator->serv = serv; - - zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); - zval_ptr_dtor(&connection_iterator); - } while (0); - - /* info */ - zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); - zend_update_property_long( - swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), (zend_long) serv->get_primary_port()->port); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv_mode); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), sock_type); + server_ctor(zserv, serv); } static PHP_METHOD(swoole_server, __destruct) {} @@ -2128,6 +2009,10 @@ static PHP_METHOD(swoole_server, __destruct) {} static PHP_METHOD(swoole_server, set) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (serv->is_worker_thread()) { + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); + RETURN_FALSE; + } if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->set", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); @@ -2184,13 +2069,24 @@ static PHP_METHOD(swoole_server, set) { serv->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } if (php_swoole_array_get_value(vht, "max_queued_bytes", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->max_queued_bytes = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } + if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->set_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX))); + if (serv->get_worker_max_concurrency() == UINT_MAX) { + serv->set_worker_max_concurrency(serv->get_max_concurrency()); + } + } + if (php_swoole_array_get_value(vht, "worker_max_concurrency", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->set_worker_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX))); + } if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { serv->enable_coroutine = zval_is_true(ztmp); } else { - serv->enable_coroutine = SWOOLE_G(enable_coroutine); + serv->enable_coroutine = SwooleG.enable_coroutine; } if (php_swoole_array_get_value(vht, "send_timeout", ztmp)) { serv->send_timeout = zval_get_double(ztmp); @@ -2210,36 +2106,15 @@ static PHP_METHOD(swoole_server, set) { serv->send_yield = serv->enable_coroutine; } if (php_swoole_array_get_value(vht, "dispatch_func", ztmp)) { - Server::DispatchFunction c_dispatch_func = nullptr; - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - c_dispatch_func = (Server::DispatchFunction) swoole_get_function(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp)); - if (c_dispatch_func) { - break; - } - } -#ifdef ZTS - if (serv->is_process_mode() && !serv->single_thread) { - php_swoole_fatal_error(E_ERROR, "option [dispatch_func] does not support with ZTS"); - } -#endif - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - sw_zend_fci_cache_persist(fci_cache); + auto fci_cache = sw_callable_create(ztmp); + if (fci_cache) { if (serv->private_data_3) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); - efree(serv->private_data_3); + sw_callable_free(serv->private_data_3); } - serv->private_data_3 = (void *) fci_cache; - c_dispatch_func = php_swoole_server_dispatch_func; - break; + serv->private_data_3 = fci_cache; + serv->dispatch_func = php_swoole_server_dispatch_func; + serv->single_thread = true; } - serv->dispatch_func = c_dispatch_func; } /** * for dispatch_mode = 1/3 @@ -2393,6 +2268,7 @@ static PHP_METHOD(swoole_server, set) { serv->http_compression = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || + php_swoole_array_get_value(vht, "compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) { zend_long level = zval_get_long(ztmp); if (level > UINT8_MAX) { @@ -2402,8 +2278,9 @@ static PHP_METHOD(swoole_server, set) { } serv->http_compression_level = level; } - if (php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { - serv->compression_min_length = zval_get_long(ztmp); + if (php_swoole_array_get_value(vht, "http_compression_min_length", ztmp) || + php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { + serv->compression_min_length = php_swoole_parse_to_size(ztmp); } #endif @@ -2422,6 +2299,9 @@ static PHP_METHOD(swoole_server, set) { } serv->upload_tmp_dir = str_v.to_std_string(); } + if (php_swoole_array_get_value(vht, "upload_max_filesize", ztmp)) { + serv->upload_max_filesize = php_swoole_parse_to_size(ztmp); + } /** * http static file handler */ @@ -2453,6 +2333,21 @@ static PHP_METHOD(swoole_server, set) { RETURN_FALSE; } } + if (php_swoole_array_get_value(vht, "http_compression_types", ztmp) || + php_swoole_array_get_value(vht, "compression_types", ztmp)) { + if (ZVAL_IS_ARRAY(ztmp)) { + zval *ztype; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype) + zend::String type(ztype); + if (type.len() > 0) { + serv->add_http_compression_type(type.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "http_compression_types must be array"); + RETURN_FALSE; + } + } /** * [static_handler] locations */ @@ -2475,7 +2370,7 @@ static PHP_METHOD(swoole_server, set) { */ if (php_swoole_array_get_value(vht, "input_buffer_size", ztmp) || php_swoole_array_get_value(vht, "buffer_input_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->input_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } /** @@ -2483,7 +2378,7 @@ static PHP_METHOD(swoole_server, set) { */ if (php_swoole_array_get_value(vht, "output_buffer_size", ztmp) || php_swoole_array_get_value(vht, "buffer_output_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->output_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } // message queue key @@ -2491,6 +2386,20 @@ static PHP_METHOD(swoole_server, set) { zend_long v = zval_get_long(ztmp); serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); } +#ifdef SW_THREAD + // bootstrap + if (php_swoole_array_get_value(vht, "bootstrap", ztmp)) { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), ztmp); + } else { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), SG(request_info).path_translated); + } + // thread arguments + if (php_swoole_array_get_value(vht, "init_arguments", ztmp)) { + server_object->init_arguments = *ztmp; + } else { + ZVAL_NULL(&server_object->init_arguments); + } +#endif if (serv->task_enable_coroutine && (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { @@ -2509,7 +2418,7 @@ static PHP_METHOD(swoole_server, set) { static PHP_METHOD(swoole_server, on) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function"); RETURN_FALSE; } @@ -2517,17 +2426,10 @@ static PHP_METHOD(swoole_server, on) { zval *name; zval *cb; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &cb) == FAILURE) { - RETURN_FALSE; - } - - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(name) + Z_PARAM_ZVAL(cb) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); zend::String _event_name_ori(name); zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false); @@ -2538,7 +2440,6 @@ static PHP_METHOD(swoole_server, on) { if (i == server_event_map.end()) { zval *port_object = server_object->property->ports.at(0); zval retval; - efree(fci_cache); sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, nullptr, "on", &retval, name, cb); RETURN_BOOL(Z_BVAL_P(&retval)); } else { @@ -2549,8 +2450,14 @@ static PHP_METHOD(swoole_server, on) { swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb); if (server_object->property->callbacks[event_type]) { - efree(server_object->property->callbacks[event_type]); + sw_callable_free(server_object->property->callbacks[event_type]); + } + + auto fci_cache = sw_callable_create(cb); + if (!fci_cache) { + RETURN_FALSE; } + server_object->property->callbacks[event_type] = fci_cache; RETURN_TRUE; @@ -2585,21 +2492,28 @@ static PHP_METHOD(swoole_server, getCallback) { static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { - php_swoole_fatal_error(E_WARNING, "server is running, can't add listener"); + if (!serv->is_worker_thread() && serv->is_started()) { + php_swoole_fatal_error(E_WARNING, "server is running, cannot add listener"); RETURN_FALSE; } char *host; size_t host_len; - long sock_type; - long port; + zend_long sock_type; + zend_long port; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &host, &host_len, &port, &sock_type) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_LONG(port) + Z_PARAM_LONG(sock_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ListenPort *ls = serv->add_port((enum swSocketType) sock_type, host, (int) port); + ListenPort *ls; + if (serv->is_worker_thread()) { + ls = serv->get_port((enum swSocketType) sock_type, host, (int) port); + } else { + ls = serv->add_port((enum swSocketType) sock_type, host, (int) port); + } if (!ls) { RETURN_FALSE; } @@ -2613,23 +2527,20 @@ extern Worker *php_swoole_process_get_and_check_worker(zval *zobject); static PHP_METHOD(swoole_server, addProcess) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { - php_swoole_fatal_error(E_WARNING, "server is running, can't add process"); + if (!serv->is_worker_thread() && serv->is_started()) { + php_swoole_fatal_error(E_WARNING, "server is running, cannot add process"); RETURN_FALSE; } - zval *process = nullptr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &process) == FAILURE) { - RETURN_FALSE; - } + int worker_id; + Worker *worker; + zval *process; - if (ZVAL_IS_NULL(process)) { - php_swoole_fatal_error(E_WARNING, "the first parameter can't be empty"); - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(process) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (!instanceof_function(Z_OBJCE_P(process), swoole_process_ce)) { + if (!ZVAL_IS_OBJECT(process) || !instanceof_function(Z_OBJCE_P(process), swoole_process_ce)) { php_swoole_fatal_error(E_ERROR, "object is not instanceof swoole_process"); RETURN_FALSE; } @@ -2641,22 +2552,31 @@ static PHP_METHOD(swoole_server, addProcess) { zval *tmp_process = (zval *) emalloc(sizeof(zval)); memcpy(tmp_process, process, sizeof(zval)); process = tmp_process; + Z_TRY_ADDREF_P(process); ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); server_object->property->user_processes.push_back(process); - Z_TRY_ADDREF_P(process); - - Worker *worker = php_swoole_process_get_and_check_worker(process); - worker->ptr = process; - - int id = serv->add_worker(worker); - if (id < 0) { - php_swoole_fatal_error(E_WARNING, "Server::add_worker() failed"); - RETURN_FALSE; + if (serv->is_worker_thread()) { + if (!serv->is_user_worker()) { + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); + RETURN_FALSE; + } + worker_id = swoole_get_process_id(); + worker = serv->get_worker(worker_id); + worker->redirect_stdin = worker->redirect_stdout = worker->redirect_stderr = 0; + worker_id -= (serv->worker_num + serv->task_worker_num); + } else { + worker = php_swoole_process_get_and_check_worker(process); + worker_id = serv->add_worker(worker); + if (worker_id < 0) { + php_swoole_fatal_error(E_WARNING, "failed to add worker"); + RETURN_FALSE; + } + worker->ptr = process; } - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), id); - RETURN_LONG(id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), worker_id); + RETURN_LONG(worker_id); } static PHP_METHOD(swoole_server, addCommand) { @@ -2669,13 +2589,12 @@ static PHP_METHOD(swoole_server, addCommand) { char *name; size_t l_name; zend_long accepted_process_types; - zend_fcall_info fci; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); + zval *zfn; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STRING(name, l_name) Z_PARAM_LONG(accepted_process_types) - Z_PARAM_FUNC(fci, *fci_cache) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (accepted_process_types & Server::Command::REACTOR_THREAD) { @@ -2683,14 +2602,19 @@ static PHP_METHOD(swoole_server, addCommand) { RETURN_FALSE; } - Server::Command::Handler fn = [fci_cache](Server *serv, const std::string &msg) { - zval *zserv = (zval *) serv->private_data_2; + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + + Server::Command::Handler fn = [cb](Server *serv, const std::string &msg) { + zval *zserv = php_swoole_server_zval_ptr(serv); zval argv[2]; argv[0] = *zserv; ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); zval return_value; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, argv, &return_value, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, argv, &return_value, false))) { php_swoole_fatal_error(E_WARNING, "%s: command handler error", ZSTR_VAL(swoole_server_ce->name)); return std::string("{\"data\": \"failed to call function\", \"code\": -1}"); } @@ -2707,8 +2631,7 @@ static PHP_METHOD(swoole_server, addCommand) { } ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); - sw_zend_fci_cache_persist(fci_cache); - server_object->property->command_callbacks.push_back(fci_cache); + server_object->property->command_callbacks.push_back(cb); RETURN_TRUE; } @@ -2716,6 +2639,15 @@ static PHP_METHOD(swoole_server, start) { zval *zserv = ZEND_THIS; Server *serv = php_swoole_server_get_and_check_server(zserv); +#ifdef SW_THREAD + if (serv->is_worker_thread()) { + zval *zsetting = sw_zend_read_and_convert_property_array(Z_OBJCE_P(ZEND_THIS), zserv, ZEND_STRL("setting"), 0); + php_swoole_unserialize((zend_string *) serv->private_data_4, zsetting); + worker_thread_fn(); + RETURN_TRUE; + } +#endif + if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv)); @@ -2727,19 +2659,72 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - if (SwooleTG.reactor) { + if (sw_reactor()) { php_swoole_fatal_error( E_WARNING, "eventLoop has already been created, unable to start %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; } - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + +#ifdef SW_THREAD + zend_string *bootstrap = nullptr; + ZendArray *thread_argv = nullptr; + + if (serv->is_thread_mode()) { + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); + + if (!ZVAL_IS_NULL(&server_object->init_arguments)) { + zval _thread_argv; + call_user_function(NULL, NULL, &server_object->init_arguments, &_thread_argv, 0, NULL); + if (ZVAL_IS_ARRAY(&_thread_argv)) { + thread_argv = ZendArray::from(Z_ARRVAL(_thread_argv)); + } + zval_ptr_dtor(&_thread_argv); + } + + serv->worker_thread_start = [bootstrap, thread_argv](const WorkerFn &fn) { + worker_thread_fn = fn; + zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); + if (thread_argv) { + thread_argv->add_ref(); + } + php_swoole_thread_start(bootstrap_copy, thread_argv); + }; + + serv->worker_thread_get_exit_status = [](pthread_t ptid) -> int { + return php_swoole_thread_get_exit_status(ptid); + }; + + serv->worker_thread_join = [](pthread_t ptid) { php_swoole_thread_join(ptid); }; + + /** + *The hook must be enabled before creating child threads. + *The stream factory and ops are global variables, not thread-local resources. + *These runtime hooks must be modified in a single-threaded environment. + */ + if (PHPCoroutine::get_hook_flags() > 0) { + PHPCoroutine::enable_hook(PHPCoroutine::get_hook_flags()); + } + } +#endif + server_object->register_callback(); server_object->on_before_start(); if (serv->start() < 0) { - php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); + php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", serv->get_startup_error_message()); + } + +#ifdef SW_THREAD + if (bootstrap) { + zend_string_release(bootstrap); } + if (thread_argv) { + thread_argv->del_ref(); + } +#endif RETURN_TRUE; } @@ -2772,7 +2757,7 @@ static PHP_METHOD(swoole_server, send) { size_t length = php_swoole_get_send_data(zdata, &data); if (length == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } @@ -2792,7 +2777,6 @@ static PHP_METHOD(swoole_server, send) { } bool ret = serv->send(fd, data, length); if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) { - zval_add_ref(zdata); php_swoole_server_send_yield(serv, fd, zdata, return_value); } else { RETURN_BOOL(ret); @@ -2823,7 +2807,7 @@ static PHP_METHOD(swoole_server, sendto) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (len == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } @@ -2882,9 +2866,13 @@ static PHP_METHOD(swoole_server, sendfile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls|ll", &fd, &filename, &len, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_LONG(fd) + Z_PARAM_STRING(filename, len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (serv->is_master()) { php_swoole_fatal_error(E_WARNING, "can't sendfile[%s] to the connections in master process", filename); @@ -2921,9 +2909,11 @@ static PHP_METHOD(swoole_server, pause) { } zend_long fd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); @@ -2940,9 +2930,11 @@ static PHP_METHOD(swoole_server, resume) { } zend_long fd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); @@ -2960,7 +2952,7 @@ static PHP_METHOD(swoole_server, stats) { array_init(return_value); add_assoc_long_ex(return_value, ZEND_STRL("start_time"), serv->gs->start_time); - add_assoc_long_ex(return_value, ZEND_STRL("connection_num"), serv->gs->connection_num); + add_assoc_long_ex(return_value, ZEND_STRL("connection_num"), serv->get_connection_num()); add_assoc_long_ex(return_value, ZEND_STRL("abort_count"), serv->gs->abort_count); add_assoc_long_ex(return_value, ZEND_STRL("accept_count"), serv->gs->accept_count); add_assoc_long_ex(return_value, ZEND_STRL("close_count"), serv->gs->close_count); @@ -2974,14 +2966,16 @@ static PHP_METHOD(swoole_server, stats) { add_assoc_long_ex(return_value, ZEND_STRL("total_recv_bytes"), serv->gs->total_recv_bytes); add_assoc_long_ex(return_value, ZEND_STRL("total_send_bytes"), serv->gs->total_send_bytes); add_assoc_long_ex(return_value, ZEND_STRL("pipe_packet_msg_id"), serv->gs->pipe_packet_msg_id); + add_assoc_long_ex(return_value, ZEND_STRL("concurrency"), serv->get_concurrency()); add_assoc_long_ex(return_value, ZEND_STRL("session_round"), serv->gs->session_round); add_assoc_long_ex(return_value, ZEND_STRL("min_fd"), serv->gs->min_fd); add_assoc_long_ex(return_value, ZEND_STRL("max_fd"), serv->gs->max_fd); - if (SwooleWG.worker) { - add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), SwooleWG.worker->request_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), SwooleWG.worker->response_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), SwooleWG.worker->dispatch_count); + if (sw_worker()) { + add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), sw_worker()->request_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), sw_worker()->response_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), sw_worker()->dispatch_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_concurrency"), sw_worker()->concurrency); } if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { @@ -2995,7 +2989,8 @@ static PHP_METHOD(swoole_server, stats) { if (serv->task_worker_num > 0) { add_assoc_long_ex(return_value, ZEND_STRL("task_idle_worker_num"), serv->get_idle_task_worker_num()); - add_assoc_long_ex(return_value, ZEND_STRL("tasking_num"), serv->get_task_count()); + add_assoc_long_ex(return_value, ZEND_STRL("tasking_num"), serv->get_tasking_num()); + add_assoc_long_ex(return_value, ZEND_STRL("task_count"), serv->gs->task_count); } add_assoc_long_ex(return_value, ZEND_STRL("coroutine_num"), Coroutine::count()); @@ -3009,18 +3004,14 @@ static PHP_METHOD(swoole_server, reload) { RETURN_FALSE; } - zend_bool only_reload_taskworker = 0; + zend_bool only_reload_task_workers = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &only_reload_taskworker) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(only_reload_task_workers) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - int sig = only_reload_taskworker ? SIGUSR2 : SIGUSR1; - if (swoole_kill(serv->gs->manager_pid, sig) < 0) { - php_swoole_sys_error(E_WARNING, "failed to send the reload signal"); - RETURN_FALSE; - } - RETURN_TRUE; + RETURN_BOOL(serv->reload(!only_reload_task_workers)); } static PHP_METHOD(swoole_server, heartbeat) { @@ -3032,9 +3023,10 @@ static PHP_METHOD(swoole_server, heartbeat) { zend_bool close_connection = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &close_connection) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(close_connection) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (serv->heartbeat_check_interval < 1) { RETURN_FALSE; @@ -3072,31 +3064,31 @@ static PHP_METHOD(swoole_server, taskwait) { RETURN_FALSE; } - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *zdata; double timeout = SW_TASKWAIT_TIMEOUT; zend_long dst_worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|dl", &zdata, &timeout, &dst_worker_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(dst_worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } - if (php_swoole_task_pack(&buf, zdata) < 0) { + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { RETURN_FALSE; } - int _dst_worker_id = (int) dst_worker_id; - TaskId task_id = buf.info.fd; + TaskId task_id = serv->get_task_id(&buf); // coroutine - if (PHPCoroutine::get_cid() >= 0) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + if (swoole_coroutine_is_in()) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); TaskCo task_co{}; @@ -3104,9 +3096,7 @@ static PHP_METHOD(swoole_server, taskwait) { task_co.count = 1; task_co.result = return_value; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) < 0) { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); + if (!serv->task(&buf, (int *) &dst_worker_id)) { RETURN_FALSE; } @@ -3117,54 +3107,26 @@ static PHP_METHOD(swoole_server, taskwait) { if (!retval) { RETURN_FALSE; } - return; - } - - uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); - sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); - network::Socket *task_notify_socket = pipe->get_socket(false); - - // clear history task - while (task_notify_socket->wait_event(0, SW_EVENT_READ) == SW_OK) { - if (task_notify_socket->read(¬ify, sizeof(notify)) <= 0) { - break; + } else { + auto retval = serv->task_sync(&buf, (int *) &dst_worker_id, timeout); + if (!retval) { + RETURN_FALSE; } - } - - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - - if (serv->gs->task_workers.dispatch_blocking(&buf, &_dst_worker_id) == SW_OK) { - while (1) { - if (task_notify_socket->wait_event((int) (timeout * 1000), SW_EVENT_READ) != SW_OK) { - break; - } - if (pipe->read(¬ify, sizeof(notify)) > 0) { - if (task_result->info.fd != task_id) { - continue; - } - zval *task_notify_data = php_swoole_task_unpack(task_result); - if (task_notify_data == nullptr) { - RETURN_FALSE; - } else { - RETVAL_ZVAL(task_notify_data, 0, 0); - efree(task_notify_data); - return; - } - break; - } else { - php_swoole_sys_error(E_WARNING, "taskwait failed"); - break; - } + zval zresult; + auto task_result = serv->get_task_result(); + if (!php_swoole_server_task_unpack(&zresult, task_result)) { + RETURN_FALSE; + } else { + RETURN_ZVAL(&zresult, 0, 0); } - } else { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); } - RETURN_FALSE; } static PHP_METHOD(swoole_server, taskWaitMulti) { + if (swoole_coroutine_is_in()) { + return ZEND_MN(swoole_server_taskCo)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (sw_unlikely(!serv->is_started())) { php_swoole_fatal_error(E_WARNING, "server is not running"); @@ -3175,16 +3137,14 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *ztasks; - zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &ztasks, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(ztasks) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); array_init(return_value); @@ -3197,13 +3157,13 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; + TaskId list_of_id[SW_MAX_CONCURRENT_TASK] = {}; uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = serv->get_task_result(); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); - Worker *worker = serv->get_worker(SwooleG.process_id); + Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); + Worker *worker = serv->get_worker(swoole_get_process_id()); File fp = swoole::make_tmpfile(); if (!fp.ready()) { @@ -3227,8 +3187,11 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { } task_notify_socket->set_block(); + zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) - TaskId task_id = php_swoole_task_pack(&buf, ztask); + + EventData buf; + TaskId task_id = php_swoole_server_task_pack(ztask, &buf); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "task pack failed"); goto _fail; @@ -3236,8 +3199,8 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { buf.info.ext_flags |= SW_TASK_WAITALL; dst_worker_id = -1; sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch_blocking(&buf, &dst_worker_id) < 0) { - php_swoole_sys_error(E_WARNING, "taskwait failed"); + if (!serv->task(&buf, &dst_worker_id, true)) { + php_swoole_sys_error(E_WARNING, "failed to dispatch task"); task_id = -1; _fail: add_index_bool(return_value, i, 0); @@ -3274,26 +3237,22 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - EventData *result; - zval *zdata; - uint32_t j; - do { - result = (EventData *) (content->str + content->offset); - TaskId task_id = result->info.fd; - zdata = php_swoole_task_unpack(result); - if (zdata == nullptr) { + EventData *result = (EventData *) (content->str + content->offset); + TaskId task_id = serv->get_task_id(result); + zval zresult; + if (!php_swoole_server_task_unpack(&zresult, result)) { goto _next; } + uint32_t j; for (j = 0; j < php_swoole_array_length(ztasks); j++) { if (list_of_id[j] == task_id) { break; } } - (void) add_index_zval(return_value, j, zdata); - efree(zdata); + (void) add_index_zval(return_value, j, &zresult); _next: - content->offset += sizeof(DataHead) + result->info.len; + content->offset += result->size(); } while (content->offset < 0 || (size_t) content->offset < content->length); // delete tmp file unlink(file_path.c_str()); @@ -3313,31 +3272,29 @@ static PHP_METHOD(swoole_server, taskCo) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); zval *ztasks; - zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &ztasks, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(ztasks) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int dst_worker_id = -1; TaskId task_id; int i = 0; uint32_t n_task = php_swoole_array_length(ztasks); - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - if (n_task >= SW_MAX_CONCURRENT_TASK) { php_swoole_fatal_error(E_WARNING, "too many concurrent tasks"); RETURN_FALSE; } - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } - int *list = (int *) ecalloc(n_task, sizeof(int)); + TaskId *list = (TaskId *) ecalloc(n_task, sizeof(TaskId)); if (list == nullptr) { RETURN_FALSE; } @@ -3347,21 +3304,21 @@ static PHP_METHOD(swoole_server, taskCo) { array_init_size(return_value, n_task); + zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) { - task_id = php_swoole_task_pack(&buf, ztask); + EventData buf; + task_id = php_swoole_server_task_pack(ztask, &buf); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "failed to pack task"); goto _fail; } buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); dst_worker_id = -1; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch(&buf, &dst_worker_id) < 0) { + if (!serv->task(&buf, &dst_worker_id)) { task_id = -1; _fail: add_index_bool(return_value, i, 0); n_task--; - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); } else { server_object->property->task_coroutine_map[task_id] = &task_co; } @@ -3380,9 +3337,12 @@ static PHP_METHOD(swoole_server, taskCo) { task_co.count = n_task; if (!task_co.co->yield_ex(timeout)) { + bool is_called_in_taskCo = strcasecmp(EX(func)->internal_function.function_name->val, "taskCo") == 0; for (uint32_t i = 0; i < n_task; i++) { if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), i)) { - add_index_bool(return_value, i, 0); + if (is_called_in_taskCo) { + add_index_bool(return_value, i, 0); + } server_object->property->task_coroutine_map.erase(list[i]); } } @@ -3399,46 +3359,43 @@ static PHP_METHOD(swoole_server, task) { zval *zdata; zend_long dst_worker_id = -1; - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache fci_cache = empty_fcall_info_cache; + zval *zfn = nullptr; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ZVAL(zdata) Z_PARAM_OPTIONAL Z_PARAM_LONG(dst_worker_id) - Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_task_pack(&buf, zdata) < 0) { + TaskId task_id = php_swoole_server_task_pack(zdata, &buf); + if (task_id < 0) { RETURN_FALSE; } if (!serv->is_worker()) { buf.info.ext_flags |= SW_TASK_NOREPLY; - } else if (fci.size) { + } else if (zfn && zval_is_true(zfn)) { buf.info.ext_flags |= SW_TASK_CALLBACK; - sw_zend_fci_cache_persist(&fci_cache); - server_object->property->task_callbacks[buf.info.fd] = fci_cache; + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + server_object->property->task_callbacks[task_id] = cb; } buf.info.ext_flags |= SW_TASK_NONBLOCK; - int _dst_worker_id = (int) dst_worker_id; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) >= 0) { - RETURN_LONG(buf.info.fd); + if (serv->task(&buf, (int *) &dst_worker_id)) { + RETURN_LONG(task_id); + } else { + RETURN_FALSE; } - - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - RETURN_FALSE; } static PHP_METHOD(swoole_server, command) { @@ -3463,28 +3420,18 @@ static PHP_METHOD(swoole_server, command) { Z_PARAM_BOOL(json_decode) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - std::string msg; - - auto result = zend::function::call("json_encode", 1, zdata); - if (!ZVAL_IS_STRING(&result.value)) { + smart_str buf = {}; + if (php_json_encode(&buf, zdata, 0) == FAILURE || !buf.s) { RETURN_FALSE; } - msg.append(Z_STRVAL(result.value), Z_STRLEN(result.value)); auto co = Coroutine::get_current_safe(); bool donot_yield = false; Server::Command::Callback fn = [co, return_value, json_decode, &donot_yield](Server *serv, const std::string &msg) { if (json_decode) { - zval argv[2]; - ZVAL_STRINGL(&argv[0], msg.c_str(), msg.length()); - ZVAL_BOOL(&argv[1], true); - auto result = zend::function::call("json_decode", 2, argv); - if (!zend_is_true(&result.value)) { - RETURN_FALSE; - } else { - ZVAL_DUP(return_value, &result.value); + if (php_json_decode(return_value, msg.c_str(), (int) msg.length(), true, 0) == FAILURE) { + RETVAL_FALSE; } - zval_dtor(&argv[0]); } else { ZVAL_STRINGL(return_value, msg.c_str(), msg.length()); } @@ -3496,10 +3443,15 @@ static PHP_METHOD(swoole_server, command) { } }; - if (!serv->command( - (uint16_t) process_id, (Server::Command::ProcessType) process_type, std::string(name, l_name), msg, fn)) { + if (!serv->command((uint16_t) process_id, + (Server::Command::ProcessType) process_type, + std::string(name, l_name), + std::string(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s)), + fn)) { + smart_str_free(&buf); RETURN_FALSE; } + smart_str_free(&buf); if (!donot_yield) { co->yield(); } @@ -3519,11 +3471,12 @@ static PHP_METHOD(swoole_server, sendMessage) { zval *zmessage; zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &zmessage, &worker_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zmessage) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (worker_id == SwooleG.process_id) { + if ((serv->is_worker() || serv->is_task_worker()) && worker_id == swoole_get_process_id()) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); RETURN_FALSE; } @@ -3533,17 +3486,11 @@ static PHP_METHOD(swoole_server, sendMessage) { } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_task_pack(&buf, zmessage) < 0) { + if (php_swoole_server_task_pack(zmessage, &buf) < 0) { RETURN_FALSE; } - buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; - - Worker *to_worker = serv->get_worker(worker_id); - SW_CHECK_RETURN(serv->send_to_worker_from_worker( - to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK)); + RETURN_BOOL(serv->send_pipe_message(worker_id, &buf)); } static PHP_METHOD(swoole_server, finish) { @@ -3565,7 +3512,7 @@ static PHP_METHOD(swoole_server, finish) { Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, nullptr)); + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, nullptr)); } static PHP_METHOD(swoole_server_task, finish) { @@ -3582,25 +3529,35 @@ static PHP_METHOD(swoole_server_task, finish) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); DataHead *info = php_swoole_server_task_get_info(ZEND_THIS); - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, (EventData *) info)); + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, (EventData *) info)); } static PHP_METHOD(swoole_server_task, pack) { - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *zdata; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_task_pack(&buf, zdata) < 0) { + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { RETURN_FALSE; } buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); - RETURN_STRINGL((char *) &buf, sizeof(buf.info) + buf.info.len); + RETURN_STRINGL((char *) &buf, buf.size()); +} + +static PHP_METHOD(swoole_server_task, unpack) { + zval *zdata; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + EventData *buf = (EventData *) Z_STRVAL_P(zdata); + if (!php_swoole_server_task_unpack(return_value, buf)) { + RETURN_FALSE; + } } static PHP_METHOD(swoole_server, bind) { @@ -3613,9 +3570,10 @@ static PHP_METHOD(swoole_server, bind) { zend_long fd = 0; zend_long uid = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &fd, &uid) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(fd) + Z_PARAM_LONG(uid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (uid > UINT32_MAX || uid < INT32_MIN) { php_swoole_fatal_error(E_WARNING, "uid can not be greater than %u or less than %d", UINT32_MAX, INT32_MIN); @@ -3643,11 +3601,12 @@ static PHP_METHOD(swoole_server, getSocket) { zend_long port = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &port) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ListenPort *lp = serv->get_port(port); + const ListenPort *lp = serv->get_port(port); php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); if (!socket_object) { @@ -3670,9 +3629,12 @@ static PHP_METHOD(swoole_server, getClientInfo) { zend_long reactor_id = -1; zend_bool dont_check_connection = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|lb", &fd, &reactor_id, &dont_check_connection) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_LONG(fd) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(reactor_id) + Z_PARAM_BOOL(dont_check_connection) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(fd); if (!conn) { @@ -3734,9 +3696,11 @@ static PHP_METHOD(swoole_server, getClientList) { zend_long start_session_id = 0; zend_long find_count = 10; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &start_session_id, &find_count) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(start_session_id) + Z_PARAM_LONG(find_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); // exceeded the maximum number of searches if (find_count > SW_MAX_FIND_COUNT) { @@ -3766,7 +3730,11 @@ static PHP_METHOD(swoole_server, getClientList) { int fd = start_fd + 1; for (; fd <= serv_max_fd; fd++) { - swoole_trace("maxfd=%d, fd=%d, find_count=%ld, start_fd=%ld", serv_max_fd, fd, find_count, start_session_id); + swoole_trace("maxfd=%d, fd=%d, find_count=" ZEND_LONG_FMT ", start_fd=" ZEND_LONG_FMT, + serv_max_fd, + fd, + find_count, + start_session_id); Connection *conn = serv->get_connection_for_iterator(fd); if (conn) { SessionId session_id = conn->session_id; @@ -3793,20 +3761,21 @@ static PHP_METHOD(swoole_server, sendwait) { zend_long fd; zval *zdata; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &fd, &zdata) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(fd) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char *data; size_t length = php_swoole_get_send_data(zdata, &data); if (length == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } if (serv->is_process_mode() || serv->is_task_worker()) { - php_swoole_fatal_error(E_WARNING, "can't sendwait"); + php_swoole_fatal_error(E_WARNING, "can only be used with base mode and must be within worker process"); RETURN_FALSE; } @@ -3844,9 +3813,11 @@ static PHP_METHOD(swoole_server, protect) { zend_long session_id; zend_bool value = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &session_id, &value) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(session_id) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(session_id); if (!conn || conn->closed) { @@ -3862,7 +3833,7 @@ static PHP_METHOD(swoole_server, getWorkerId) { if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(SwooleG.process_id); + RETURN_LONG(swoole_get_process_id()); } } @@ -3874,17 +3845,14 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { } zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { - RETURN_FALSE; - } - Worker *worker; - if (worker_id == -1) { - worker = SwooleWG.worker; - } else { - worker = serv->get_worker(worker_id); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; + Worker *worker = serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } else { @@ -3895,10 +3863,14 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { static PHP_METHOD(swoole_server, getWorkerPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { - RETURN_FALSE; - } - Worker *worker = worker_id < 0 ? SwooleWG.worker : serv->get_worker(worker_id); + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; + Worker *worker = serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } @@ -3907,27 +3879,17 @@ static PHP_METHOD(swoole_server, getWorkerPid) { static PHP_METHOD(swoole_server, getManagerPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->manager_pid); + RETURN_LONG(serv->get_manager_pid()); } static PHP_METHOD(swoole_server, getMasterPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->master_pid); + RETURN_LONG(serv->get_master_pid()); } static PHP_METHOD(swoole_server, shutdown) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->is_started())) { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - if (swoole_kill(serv->gs->master_pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "failed to shutdown, kill(%d, SIGTERM) failed", serv->gs->master_pid); - RETURN_FALSE; - } else { - RETURN_TRUE; - } + RETURN_BOOL(serv->shutdown()); } static PHP_METHOD(swoole_server, stop) { @@ -3938,37 +3900,23 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - long worker_id = SwooleG.process_id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lb", &worker_id, &wait_reactor) == FAILURE) { - RETURN_FALSE; - } + zend_long worker_id = -1; - if (worker_id == SwooleG.process_id && wait_reactor == 0) { - if (SwooleTG.reactor != nullptr) { - SwooleTG.reactor->defer( - [](void *data) { - Reactor *reactor = (Reactor *) data; - reactor->running = false; - }, - SwooleTG.reactor); - } - serv->running = false; - } else { - Worker *worker = serv->get_worker(worker_id); - if (worker == nullptr) { - RETURN_FALSE; - } else if (swoole_kill(worker->pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "swKill(%d, SIGTERM) failed", worker->pid); - RETURN_FALSE; - } - } - RETURN_TRUE; + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + Z_PARAM_BOOL(wait_reactor) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; + + RETURN_BOOL(serv->kill_worker(worker_id, wait_reactor)); } // swoole_connection_iterator static PHP_METHOD(swoole_connection_iterator, __construct) { - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->connections"); + zend_throw_error(NULL, "please use the Swoole\\Server->connections"); return; } @@ -4020,32 +3968,36 @@ static PHP_METHOD(swoole_connection_iterator, key) { static PHP_METHOD(swoole_connection_iterator, count) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); if (iterator->port) { - RETURN_LONG(iterator->port->gs->connection_num); + RETURN_LONG(iterator->port->get_connection_num()); } else { - RETURN_LONG(iterator->serv->gs->connection_num); + RETURN_LONG(iterator->serv->get_connection_num()); } } static PHP_METHOD(swoole_connection_iterator, offsetExists) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, "exists", &retval, zfd); RETVAL_BOOL(Z_BVAL_P(&retval)); } static PHP_METHOD(swoole_connection_iterator, offsetGet) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, "getClientInfo", &retval, zfd); RETVAL_ZVAL(&retval, 0, 0); } diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 1a3891c6758..9e5fdadb0a8 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -10,11 +10,16 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_server.h" +#include "php_swoole_call_stack.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_server_port_arginfo.h" +END_EXTERN_C() using namespace swoole; @@ -33,7 +38,8 @@ static std::unordered_map server_port_event_map({ { "bufferfull", ServerPortEvent(SW_SERVER_CB_onBufferFull, "BufferFull") }, { "bufferempty", ServerPortEvent(SW_SERVER_CB_onBufferEmpty, "BufferEmpty") }, { "request", ServerPortEvent(SW_SERVER_CB_onRequest, "Request") }, - { "handshake", ServerPortEvent(SW_SERVER_CB_onHandShake, "Handshake") }, + { "handshake", ServerPortEvent(SW_SERVER_CB_onHandshake, "Handshake") }, + { "beforehandshakeresponse", ServerPortEvent(SW_SERVER_CB_onBeforeHandshakeResponse, "BeforeHandshakeResponse") }, { "open", ServerPortEvent(SW_SERVER_CB_onOpen, "Open") }, { "message", ServerPortEvent(SW_SERVER_CB_onMessage, "Message") }, { "disconnect", ServerPortEvent(SW_SERVER_CB_onDisconnect, "Disconnect") }, @@ -87,9 +93,9 @@ void php_swoole_server_port_deref(zend_object *object) { ServerPortProperty *property = &server_port->property; if (property->serv) { for (int j = 0; j < PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM; j++) { - if (property->caches[j]) { - efree(property->caches[j]); - property->caches[j] = nullptr; + if (property->callbacks[j]) { + sw_callable_free(property->callbacks[j]); + property->callbacks[j] = nullptr; } } property->serv = nullptr; @@ -97,10 +103,9 @@ void php_swoole_server_port_deref(zend_object *object) { ListenPort *port = server_port->port; if (port) { - if (port->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) port->protocol.private_data); - efree(port->protocol.private_data); - port->protocol.private_data = nullptr; + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); + port->protocol.private_data_1 = nullptr; } server_port->port = nullptr; } @@ -132,41 +137,25 @@ static PHP_METHOD(swoole_server_port, getSocket); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_getCallback, 0, 0, 1) - ZEND_ARG_INFO(0, event_name) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_server_port_methods[] = { - PHP_ME(swoole_server_port, __construct, arginfo_swoole_void, ZEND_ACC_PRIVATE) - PHP_ME(swoole_server_port, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, set, arginfo_swoole_server_port_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, on, arginfo_swoole_server_port_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, getCallback, arginfo_swoole_server_port_getCallback, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, __construct, arginfo_class_Swoole_Server_Port___construct, ZEND_ACC_PRIVATE) + PHP_ME(swoole_server_port, __destruct, arginfo_class_Swoole_Server_Port___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, set, arginfo_class_Swoole_Server_Port_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, on, arginfo_class_Swoole_Server_Port_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, getCallback, arginfo_class_Swoole_Server_Port_getCallback, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server_port, getSocket, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, getSocket, arginfo_class_Swoole_Server_Port_getSocket, ZEND_ACC_PUBLIC) #endif PHP_FE_END }; // clang-format on void php_swoole_server_port_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_server_port, "Swoole\\Server\\Port", "swoole_server_port", nullptr, swoole_server_port_methods); + SW_INIT_CLASS_ENTRY(swoole_server_port, "Swoole\\Server\\Port", nullptr, swoole_server_port_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_port); +#endif SW_SET_CLASS_CLONEABLE(swoole_server_port, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_port, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_port, @@ -182,38 +171,34 @@ void php_swoole_server_port_minit(int module_number) { zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBufferFull"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBufferEmpty"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onRequest"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onHandShake"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onHandshake"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onOpen"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onMessage"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onDisconnect"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBeforeHandshakeResponse"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_server_port_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("connections"), ZEND_ACC_PUBLIC); } /** - * [Master-Process] + * [Master/Worker] */ -static ssize_t php_swoole_server_length_func(Protocol *protocol, - network::Socket *conn, - const char *data, - uint32_t length) { - Server *serv = (Server *) protocol->private_data_2; - serv->lock(); - - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; +static ssize_t php_swoole_server_length_func(const Protocol *protocol, network::Socket *conn, PacketLength *pl) { + zend::Callable *cb = (zend::Callable *) protocol->private_data_1; zval zdata; zval retval; ssize_t ret = -1; - // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); - if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { + ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), 1, &zdata, &retval);); + if (UNEXPECTED(call_result) != SUCCESS) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { ret = zval_get_long(&retval); @@ -221,8 +206,6 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, } zval_ptr_dtor(&zdata); - serv->unlock(); - /* the exception should only be thrown after unlocked */ if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); @@ -232,8 +215,8 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, } static PHP_METHOD(swoole_server_port, __construct) { - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->listen method"); - return; + zend_throw_error(NULL, "please use the Swoole\\Server->listen method"); + RETURN_FALSE; } static PHP_METHOD(swoole_server_port, __destruct) {} @@ -286,7 +269,7 @@ static PHP_METHOD(swoole_server_port, set) { port->backlog = SW_MAX(0, SW_MIN(v, UINT16_MAX)); } if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->socket_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->socket_buffer_size <= 0) { port->socket_buffer_size = INT_MAX; @@ -296,7 +279,7 @@ static PHP_METHOD(swoole_server_port, set) { * !!! Don't set this option, for tests only. */ if (php_swoole_array_get_value(vht, "kernel_socket_recv_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->kernel_socket_recv_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->kernel_socket_recv_buffer_size <= 0) { port->kernel_socket_recv_buffer_size = INT_MAX; @@ -306,7 +289,7 @@ static PHP_METHOD(swoole_server_port, set) { * !!! Don't set this option, for tests only. */ if (php_swoole_array_get_value(vht, "kernel_socket_send_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->kernel_socket_send_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->kernel_socket_send_buffer_size <= 0) { port->kernel_socket_send_buffer_size = INT_MAX; @@ -318,11 +301,11 @@ static PHP_METHOD(swoole_server_port, set) { port->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX)); } if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->buffer_high_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } if (php_swoole_array_get_value(vht, "buffer_low_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->buffer_low_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } // server: tcp_nodelay @@ -387,7 +370,6 @@ static PHP_METHOD(swoole_server_port, set) { if (php_swoole_array_get_value(vht, "open_websocket_pong_frame", ztmp)) { port->open_websocket_pong_frame = zval_is_true(ztmp); } -#ifdef SW_USE_HTTP2 // http2 protocol if (php_swoole_array_get_value(vht, "open_http2_protocol", ztmp)) { port->open_http2_protocol = zval_is_true(ztmp); @@ -395,7 +377,6 @@ static PHP_METHOD(swoole_server_port, set) { port->open_http_protocol = 1; } } -#endif // buffer: mqtt protocol if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { port->open_mqtt_protocol = zval_is_true(ztmp); @@ -405,7 +386,8 @@ static PHP_METHOD(swoole_server_port, set) { port->open_redis_protocol = zval_get_long(ztmp); } if (php_swoole_array_get_value(vht, "max_idle_time", ztmp)) { - port->max_idle_time = zval_get_double(ztmp); + double v = zval_get_double(ztmp); + port->max_idle_time = SW_MAX(v, SW_TIMER_MIN_SEC); } // tcp_keepidle if (php_swoole_array_get_value(vht, "tcp_keepidle", ztmp)) { @@ -464,46 +446,24 @@ static PHP_METHOD(swoole_server_port, set) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - Protocol::LengthFunc func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))); - if (func != nullptr) { - port->protocol.get_package_length = func; - break; - } - } -#ifdef ZTS - Server *serv = property->serv; - if (serv->is_process_mode() && !serv->single_thread) { - php_swoole_fatal_error(E_ERROR, "option [package_length_func] does not support with ZTS"); - } -#endif - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); + auto cb = sw_callable_create(ztmp); + if (cb) { port->protocol.get_package_length = php_swoole_server_length_func; - if (port->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) port->protocol.private_data); - efree(port->protocol.private_data); + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); } - sw_zend_fci_cache_persist(fci_cache); - port->protocol.private_data = fci_cache; - break; + port->protocol.private_data_1 = cb; + port->protocol.package_length_size = 0; + port->protocol.package_length_type = '\0'; + port->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; + property->serv->single_thread = true; } - - port->protocol.package_length_size = 0; - port->protocol.package_length_type = '\0'; - port->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; } /** * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } @@ -634,22 +594,16 @@ static PHP_METHOD(swoole_server_port, on) { ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); Server *serv = property->serv; - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "can't register event callback function after server started"); RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name, &len, &cb) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, len) + Z_PARAM_ZVAL(cb) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); bool found = false; for (auto i = server_port_event_map.begin(); i != server_port_event_map.end(); i++) { @@ -662,13 +616,16 @@ static PHP_METHOD(swoole_server_port, on) { std::string property_name = std::string("on") + i->second.name; zend_update_property( swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb); - property->callbacks[index] = - sw_zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name.c_str(), property_name.length(), 0); - sw_copy_to_stack(property->callbacks[index], property->_callbacks[index]); - if (property->caches[index]) { - efree(property->caches[index]); + + if (property->callbacks[index]) { + sw_callable_free(property->callbacks[index]); + } + + auto fci_cache = sw_callable_create(cb); + if (!fci_cache) { + RETURN_FALSE; } - property->caches[index] = fci_cache; + property->callbacks[index] = fci_cache; if (index == SW_SERVER_CB_onConnect && !serv->onConnect) { serv->onConnect = php_swoole_server_onConnect; @@ -686,7 +643,6 @@ static PHP_METHOD(swoole_server_port, on) { if (!found) { php_swoole_error(E_WARNING, "unknown event types[%s]", name); - efree(fci_cache); RETURN_FALSE; } RETURN_TRUE; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index dbbd7e03575..0c762eded08 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -12,11 +12,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" + #include "swoole_string.h" #include "swoole_socket.h" #include "swoole_util.h" @@ -27,7 +29,14 @@ #include +BEGIN_EXTERN_C() +#include "stubs/php_swoole_socket_coro_arginfo.h" +END_EXTERN_C() + +using swoole::HttpProxy; +using swoole::PacketLength; using swoole::Protocol; +using swoole::Socks5Proxy; using swoole::coroutine::Socket; using swoole::network::Address; @@ -39,6 +48,7 @@ static zend_object_handlers swoole_socket_coro_exception_handlers; struct SocketObject { Socket *socket; + zval zstream; bool reference; zend_object std; }; @@ -50,6 +60,7 @@ static PHP_METHOD(swoole_socket_coro, listen); static PHP_METHOD(swoole_socket_coro, accept); static PHP_METHOD(swoole_socket_coro, connect); static PHP_METHOD(swoole_socket_coro, checkLiveness); +static PHP_METHOD(swoole_socket_coro, getBoundCid); static PHP_METHOD(swoole_socket_coro, peek); static PHP_METHOD(swoole_socket_coro, recv); static PHP_METHOD(swoole_socket_coro, send); @@ -76,184 +87,85 @@ static PHP_METHOD(swoole_socket_coro, close); static PHP_METHOD(swoole_socket_coro, cancel); static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); +static PHP_METHOD(swoole_socket_coro, isClosed); +static PHP_METHOD(swoole_socket_coro, import); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_construct, 0, 0, 2) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, protocol) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_bind, 0, 0, 1) - ZEND_ARG_INFO(0, address) - ZEND_ARG_INFO(0, port) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_listen, 0, 0, 0) - ZEND_ARG_INFO(0, backlog) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_accept, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_checkLiveness, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_peek, 0, 0, 0) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recvPacket, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_readVector, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_readVectorAll, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_writeVector, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_writeVectorAll, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_sendFile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recvfrom, 0, 0, 1) - ZEND_ARG_INFO(1, peername) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_getOption, 0, 0, 2) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, opt_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_setOption, 0, 0, 3) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, opt_name) - ZEND_ARG_INFO(0, opt_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_setProtocol, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, addr) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_cancel, 0, 0, 0) - ZEND_ARG_INFO(0, event) -ZEND_END_ARG_INFO() - - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_shutdown, 0, 0, 0) - ZEND_ARG_INFO(0, how) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_socket_coro_methods[] = { - PHP_ME(swoole_socket_coro, __construct, arginfo_swoole_socket_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, bind, arginfo_swoole_socket_coro_bind, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, listen, arginfo_swoole_socket_coro_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, accept, arginfo_swoole_socket_coro_accept, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, connect, arginfo_swoole_socket_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, checkLiveness, arginfo_swoole_socket_coro_checkLiveness, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, peek, arginfo_swoole_socket_coro_peek, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recv, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvAll, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvLine, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvWithBuffer, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvPacket, arginfo_swoole_socket_coro_recvPacket, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, send, arginfo_swoole_socket_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, readVector, arginfo_swoole_socket_coro_readVector, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, readVectorAll, arginfo_swoole_socket_coro_readVectorAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, writeVector, arginfo_swoole_socket_coro_writeVector, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, writeVectorAll,arginfo_swoole_socket_coro_writeVectorAll,ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendFile, arginfo_swoole_socket_coro_sendFile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendAll, arginfo_swoole_socket_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvfrom, arginfo_swoole_socket_coro_recvfrom, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendto, arginfo_swoole_socket_coro_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getOption, arginfo_swoole_socket_coro_getOption, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, setProtocol, arginfo_swoole_socket_coro_setProtocol, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, setOption, arginfo_swoole_socket_coro_setOption, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, __construct, arginfo_class_Swoole_Coroutine_Socket___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, bind, arginfo_class_Swoole_Coroutine_Socket_bind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, listen, arginfo_class_Swoole_Coroutine_Socket_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, accept, arginfo_class_Swoole_Coroutine_Socket_accept, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, connect, arginfo_class_Swoole_Coroutine_Socket_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, checkLiveness, arginfo_class_Swoole_Coroutine_Socket_checkLiveness, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getBoundCid, arginfo_class_Swoole_Coroutine_Socket_getBoundCid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, peek, arginfo_class_Swoole_Coroutine_Socket_peek, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recv, arginfo_class_Swoole_Coroutine_Socket_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvAll, arginfo_class_Swoole_Coroutine_Socket_recvAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvLine, arginfo_class_Swoole_Coroutine_Socket_recvLine, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvWithBuffer, arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvPacket, arginfo_class_Swoole_Coroutine_Socket_recvPacket, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, send, arginfo_class_Swoole_Coroutine_Socket_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, readVector, arginfo_class_Swoole_Coroutine_Socket_readVector, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, readVectorAll, arginfo_class_Swoole_Coroutine_Socket_readVectorAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, writeVector, arginfo_class_Swoole_Coroutine_Socket_writeVector, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, writeVectorAll, arginfo_class_Swoole_Coroutine_Socket_writeVectorAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendFile, arginfo_class_Swoole_Coroutine_Socket_sendFile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendAll, arginfo_class_Swoole_Coroutine_Socket_sendAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvfrom, arginfo_class_Swoole_Coroutine_Socket_recvfrom, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendto, arginfo_class_Swoole_Coroutine_Socket_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getOption, arginfo_class_Swoole_Coroutine_Socket_getOption, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, setProtocol, arginfo_class_Swoole_Coroutine_Socket_setProtocol, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, setOption, arginfo_class_Swoole_Coroutine_Socket_setOption, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_socket_coro, sslHandshake, arginfo_swoole_void, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_socket_coro, shutdown, arginfo_swoole_socket_coro_shutdown, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, cancel, arginfo_swoole_socket_coro_cancel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getsockname, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sslHandshake, arginfo_class_Swoole_Coroutine_Socket_sslHandshake, ZEND_ACC_PUBLIC) +#endif + PHP_ME(swoole_socket_coro, shutdown, arginfo_class_Swoole_Coroutine_Socket_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, cancel, arginfo_class_Swoole_Coroutine_Socket_cancel, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, close, arginfo_class_Swoole_Coroutine_Socket_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getpeername, arginfo_class_Swoole_Coroutine_Socket_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, import, arginfo_class_Swoole_Coroutine_Socket_import, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on -#define SW_BAD_SOCKET ((Socket *) -1) #define swoole_get_socket_coro(_sock, _zobject) \ - SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ + SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ if (UNEXPECTED(!sock->socket)) { \ - php_swoole_fatal_error(E_ERROR, "you must call Socket constructor first"); \ + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); \ } \ - if (UNEXPECTED(_sock->socket == SW_BAD_SOCKET)) { \ + if (UNEXPECTED(_sock->socket->is_closed())) { \ zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL("errCode"), EBADF); \ zend_update_property_string( \ swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL("errMsg"), strerror(EBADF)); \ RETURN_FALSE; \ } -static sw_inline SocketObject *php_swoole_socket_coro_fetch_object(zend_object *obj) { +static sw_inline SocketObject *socket_coro_fetch_object(zend_object *obj) { return (SocketObject *) ((char *) obj - swoole_socket_coro_handlers.offset); } -static void php_swoole_socket_coro_free_object(zend_object *object) { - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); - if (!sock->reference && sock->socket && sock->socket != SW_BAD_SOCKET) { - sock->socket->close(); +/** + * cannot execute close in the destructor, it may be shutting down, + * executing close will try to resume other coroutines. + */ +static void socket_coro_free_object(zend_object *object) { + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + if (!sock->reference && sock->socket) { + if (!Z_ISUNDEF(sock->zstream)) { + sock->socket->move_fd(); + zval_ptr_dtor(&sock->zstream); + } delete sock->socket; } zend_object_std_dtor(&sock->std); } -static zend_object *php_swoole_socket_coro_create_object(zend_class_entry *ce) { +static zend_object *socket_coro_create_object(zend_class_entry *ce) { SocketObject *sock = (SocketObject *) zend_object_alloc(sizeof(SocketObject), ce); zend_object_std_init(&sock->std, ce); /* Even if you don't use properties yourself you should still call object_properties_init(), @@ -264,7 +176,7 @@ static zend_object *php_swoole_socket_coro_create_object(zend_class_entry *ce) { return &sock->std; } -static void swoole_socket_coro_register_constants(int module_number) { +static void socket_coro_register_constants(int module_number) { REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("AF_INET", AF_INET, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("AF_INET6", AF_INET6, CONST_CS | CONST_PERSISTENT); @@ -801,45 +713,48 @@ static void swoole_socket_coro_register_constants(int module_number) { } void php_swoole_socket_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_socket_coro, "Swoole\\Coroutine\\Socket", nullptr, "Co\\Socket", swoole_socket_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", "Co\\Socket", swoole_socket_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_socket_coro, - php_swoole_socket_coro_create_object, - php_swoole_socket_coro_free_object, - SocketObject, - std); - - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_socket_coro, socket_coro_create_object, socket_coro_free_object, SocketObject, std); + + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); +#ifdef SWOOLE_SOCKETS_SUPPORT + zend_declare_property_bool(swoole_socket_coro_ce, ZEND_STRL("__ext_sockets_nonblock"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("__ext_sockets_timeout"), 0, ZEND_ACC_PUBLIC); +#endif + SW_INIT_CLASS_ENTRY_EX(swoole_socket_coro_exception, "Swoole\\Coroutine\\Socket\\Exception", - nullptr, "Co\\Socket\\Exception", nullptr, swoole_exception); if (!zend_hash_str_find_ptr(&module_registry, ZEND_STRL("sockets"))) { - swoole_socket_coro_register_constants(module_number); + socket_coro_register_constants(module_number); } #ifdef ECANCELED SW_REGISTER_LONG_CONSTANT("SOCKET_ECANCELED", ECANCELED); #endif +#ifdef TCP_INFO + SW_REGISTER_LONG_CONSTANT("TCP_INFO", TCP_INFO); +#endif } -static sw_inline void swoole_socket_coro_sync_properties(zval *zobject, SocketObject *sock) { +static sw_inline void socket_coro_sync_properties(zval *zobject, SocketObject *sock) { zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sock->socket->errCode); zend_update_property_string(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), sock->socket->errMsg); } -static void sw_inline php_swoole_init_socket(zval *zobject, SocketObject *sock) { +static void sw_inline socket_coro_init(zval *zobject, SocketObject *sock) { sock->socket->set_zero_copy(true); sock->socket->set_buffer_allocator(sw_zend_string_allocator()); zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), sock->socket->get_fd()); @@ -852,17 +767,17 @@ static void sw_inline php_swoole_init_socket(zval *zobject, SocketObject *sock) } SW_API bool php_swoole_export_socket(zval *zobject, Socket *_socket) { - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); if (!object) { return false; } - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->reference = 1; sock->socket = _socket; ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); + socket_coro_init(zobject, sock); return true; } @@ -876,12 +791,12 @@ SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type) { return php_swoole_create_socket_from_fd(new_fd, type); } -SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { +SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { zval zobject; - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); - sock->socket = new Socket(fd, type); + sock->socket = new Socket(type); if (UNEXPECTED(sock->socket->get_fd() < 0)) { php_swoole_sys_error(E_WARNING, "new Socket() failed"); delete sock->socket; @@ -891,22 +806,68 @@ SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType t } ZVAL_OBJ(&zobject, object); - php_swoole_init_socket(&zobject, sock); + socket_coro_init(&zobject, sock); return object; } +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg) { + swoole_set_last_error(code); + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), code); + zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), msg); +} + +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code) { + php_swoole_socket_set_error_properties(zobject, code, swoole_strerror(code)); +} + +SW_API void php_swoole_socket_set_error_properties(zval *zobject, Socket *socket) { + php_swoole_socket_set_error_properties(zobject, socket->errCode, socket->errMsg); +} + +static zend_object *create_socket_object(Socket *socket) { + zval zobject; + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + + sock->socket = socket; + if (UNEXPECTED(sock->socket->get_fd() < 0)) { + php_swoole_sys_error(E_WARNING, "new Socket() failed"); + delete sock->socket; + sock->socket = nullptr; + OBJ_RELEASE(object); + return nullptr; + } + + ZVAL_OBJ(&zobject, object); + socket_coro_init(&zobject, sock); + return object; +} + +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { + return create_socket_object(new Socket(fd, type)); +} + +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol) { + return create_socket_object(new Socket(fd, _domain, _type, _protocol)); +} + SW_API Socket *php_swoole_get_socket(zval *zobject) { SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(zobject)); return sock->socket; } +SW_API bool php_swoole_socket_is_closed(zval *zobject) { + SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(zobject)); + return _sock->socket == nullptr || _sock->socket->is_closed(); +} + SW_API void php_swoole_init_socket_object(zval *zobject, Socket *socket) { - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->socket = socket; ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); + socket_coro_init(zobject, sock); } SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { @@ -965,10 +926,10 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = - [](Protocol *protocol, swoole::network::Socket *conn, const char *data, uint32_t size) { - const uint8_t *p = (const uint8_t *) data; + [](const Protocol *protocol, swoole::network::Socket *conn, PacketLength *pl) { + const uint8_t *p = (const uint8_t *) pl->buf; ssize_t length = 0; - if (size >= FCGI_HEADER_LEN) { + if (pl->buf_size >= FCGI_HEADER_LEN) { length = ((p[4] << 8) | p[5]) + p[6]; if (length > FCGI_MAX_LENGTH) { length = -1; @@ -1015,40 +976,23 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - do { - Protocol::LengthFunc func; - if (Z_TYPE_P(ztmp) == IS_STRING && - (func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))))) { - sock->protocol.get_package_length = func; - } else { - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", func_name); - efree(func_name); - efree(fci_cache); - ret = false; - break; - } - efree(func_name); - sock->protocol.get_package_length = php_swoole_length_func; - if (sock->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) sock->protocol.private_data); - efree(sock->protocol.private_data); - } - sw_zend_fci_cache_persist(fci_cache); - sock->protocol.private_data = fci_cache; + auto cb = sw_callable_create(ztmp); + if (cb) { + sock->protocol.get_package_length = php_swoole_length_func; + if (sock->protocol.private_data_1) { + sw_callable_free(sock->protocol.private_data_1); } + sock->protocol.private_data_1 = cb; sock->protocol.package_length_size = 0; sock->protocol.package_length_type = '\0'; sock->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; - } while (0); + } } /** * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); sock->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } else { sock->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; @@ -1057,6 +1001,209 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { return ret; } +SW_API bool php_swoole_socket_set(Socket *cli, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + bool ret = true; + + /** + * timeout + */ + if (php_swoole_array_get_value(vht, "timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp)); + } + if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_CONNECT); + } + if (php_swoole_array_get_value(vht, "read_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_READ); + } + if (php_swoole_array_get_value(vht, "write_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_WRITE); + } + std::string _bind_address; + int _bind_port = 0; + if (php_swoole_array_get_value(vht, "bind_port", ztmp)) { + zend_long v = zval_get_long(ztmp); + _bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + if (php_swoole_array_get_value(vht, "bind_address", ztmp)) { + zend::String tmp = ztmp; + _bind_address = tmp.to_std_string(); + } + if (!_bind_address.empty() && !cli->bind(_bind_address, _bind_port)) { + ret = false; + } + /** + * socket send/recv buffer size + */ + if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { + zend_long size = php_swoole_parse_to_size(ztmp); + if (size <= 0) { + php_swoole_fatal_error(E_WARNING, "socket buffer size must be greater than 0, got " ZEND_LONG_FMT, size); + ret = false; + } else { + cli->set_option(SOL_SOCKET, SO_RCVBUF, size) && cli->set_option(SOL_SOCKET, SO_SNDBUF, size); + } + } + /** + * client: tcp_nodelay + */ + if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { + if (cli->get_type() == SW_SOCK_TCP || cli->get_type() != SW_SOCK_TCP6) { + cli->get_socket()->set_tcp_nodelay(zval_is_true(ztmp)); + } + } + /** + * openssl and protocol options + */ + if (!php_swoole_socket_set_protocol(cli, zset)) { + ret = false; + } + /** + * socks5 proxy + */ + if (php_swoole_array_get_value(vht, "socks5_host", ztmp)) { + zend::String host(ztmp); + if (php_swoole_array_get_value(vht, "socks5_port", ztmp)) { + if (cli->socks5_proxy == nullptr) { + cli->socks5_proxy = new Socks5Proxy(); + } + cli->socks5_proxy->host = host.to_std_string(); + cli->socks5_proxy->port = zval_get_long(ztmp); + cli->socks5_proxy->dns_tunnel = 1; + if (php_swoole_array_get_value(vht, "socks5_username", ztmp)) { + zend::String username(ztmp); + if (username.len() > 0 && php_swoole_array_get_value(vht, "socks5_password", ztmp)) { + zend::String password(ztmp); + if (password.len() > 0) { + cli->socks5_proxy->method = 0x02; + cli->socks5_proxy->username = username.to_std_string(); + cli->socks5_proxy->password = password.to_std_string(); + } + } else { + php_swoole_fatal_error(E_WARNING, "socks5_password should not be null"); + ret = false; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); + ret = false; + } + } + /** + * http proxy + */ + else if (php_swoole_array_get_value(vht, "http_proxy_host", ztmp)) { + zend::String host(ztmp); + if (php_swoole_array_get_value(vht, "http_proxy_port", ztmp)) { + if (cli->http_proxy == nullptr) { + cli->http_proxy = new HttpProxy(); + } + cli->http_proxy->proxy_host = host.to_std_string(); + cli->http_proxy->proxy_port = zval_get_long(ztmp); + if (php_swoole_array_get_value(vht, "http_proxy_username", ztmp) || + php_swoole_array_get_value(vht, "http_proxy_user", ztmp)) { + zend::String username(ztmp); + if (username.len() > 0 && php_swoole_array_get_value(vht, "http_proxy_password", ztmp)) { + zend::String password(ztmp); + if (password.len() > 0) { + cli->http_proxy->username = username.to_std_string(); + cli->http_proxy->password = password.to_std_string(); + } + } else { + php_swoole_fatal_error(E_WARNING, "http_proxy_password should not be null"); + ret = false; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); + ret = false; + } + } + + return ret; +} + +#ifdef SW_USE_OPENSSL +SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + + if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->protocols = v; + } + if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { + sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); + } else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) { + sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) == 0) { + sock->get_ssl_context()->cert_file = str_v.to_std_string(); + } else { + php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); + return false; + } + } + if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) == 0) { + sock->get_ssl_context()->key_file = str_v.to_std_string(); + } else { + php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); + return false; + } + } + if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { + php_swoole_fatal_error(E_WARNING, "ssl require key file"); + } else if (!sock->get_ssl_context()->key_file.empty() && sock->get_ssl_context()->cert_file.empty()) { + php_swoole_fatal_error(E_WARNING, "ssl require cert file"); + } + if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { + sock->get_ssl_context()->passphrase = zend::String(ztmp).to_std_string(); + } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { + sock->get_ssl_context()->tls_host_name = zend::String(ztmp).to_std_string(); + /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ + sock->get_ssl_context()->disable_tls_host_name = sock->get_ssl_context()->tls_host_name.empty(); + } +#endif + if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { + sock->get_ssl_context()->verify_peer = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { + sock->get_ssl_context()->allow_self_signed = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { + sock->get_ssl_context()->cafile = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { + sock->get_ssl_context()->capath = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } + if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { + sock->get_ssl_context()->ciphers = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) { + sock->get_ssl_context()->ecdh_curve = zend::String(ztmp).to_std_string(); + } +#ifdef OPENSSL_IS_BORINGSSL + if (php_swoole_array_get_value(vht, "ssl_grease", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->grease = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } +#endif + return true; +} +#endif + PHP_FUNCTION(swoole_coroutine_socketpair) { zend_long domain, type, protocol; php_socket_t pair[2]; @@ -1068,13 +1215,13 @@ PHP_FUNCTION(swoole_coroutine_socketpair) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) { - php_swoole_error(E_WARNING, "failed to create sockets: [%d]: %s", errno, strerror(errno)); + php_swoole_sys_error(E_WARNING, "failed to create socket"); RETURN_FALSE; } php_swoole_check_reactor(); - auto sock_type = swoole::network::Socket::convert_to_type(domain, type, protocol); + auto sock_type = swoole::network::Socket::convert_to_type(domain, type); zend_object *s1 = php_swoole_create_socket_from_fd(pair[0], sock_type); if (s1 == nullptr) { @@ -1106,20 +1253,22 @@ static PHP_METHOD(swoole_socket_coro, __construct) { Z_PARAM_LONG(protocol) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (sock->socket) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } - if (EXPECTED(!sock->socket)) { - php_swoole_check_reactor(); - sock->socket = new Socket((int) domain, (int) type, (int) protocol); - if (UNEXPECTED(sock->socket->get_fd() < 0)) { - zend_throw_exception_ex( - swoole_socket_coro_exception_ce, errno, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); - delete sock->socket; - sock->socket = nullptr; - RETURN_FALSE; - } - php_swoole_init_socket(ZEND_THIS, sock); + php_swoole_check_reactor(); + sock->socket = new Socket((int) domain, (int) type, (int) protocol); + if (UNEXPECTED(sock->socket->get_fd() < 0)) { + zend_throw_exception_ex( + swoole_socket_coro_exception_ce, errno, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); + delete sock->socket; + sock->socket = nullptr; + RETURN_FALSE; } + socket_coro_init(ZEND_THIS, sock); } static PHP_METHOD(swoole_socket_coro, bind) { @@ -1136,7 +1285,7 @@ static PHP_METHOD(swoole_socket_coro, bind) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->bind(std::string(address, l_address), port)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1153,7 +1302,7 @@ static PHP_METHOD(swoole_socket_coro, listen) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->listen(backlog)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1171,19 +1320,18 @@ static PHP_METHOD(swoole_socket_coro, accept) { Socket *conn = sock->socket->accept(timeout); if (conn) { - zend_object *client = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *client_sock = (SocketObject *) php_swoole_socket_coro_fetch_object(client); + zend_object *client = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *client_sock = (SocketObject *) socket_coro_fetch_object(client); client_sock->socket = conn; ZVAL_OBJ(return_value, &client_sock->std); - if (conn->protocol.private_data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(*fci_cache)); - *fci_cache = *(zend_fcall_info_cache *) conn->protocol.private_data; - sw_zend_fci_cache_persist(fci_cache); - conn->protocol.private_data = fci_cache; + socket_coro_init(return_value, client_sock); + // It must be copied once to avoid destroying the function when the connection closes. + if (sock->socket->protocol.private_data_1) { + zend::Callable *cb = (zend::Callable *) sock->socket->protocol.private_data_1; + conn->protocol.private_data_1 = cb->dup(); } - php_swoole_init_socket(return_value, client_sock); } else { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } } @@ -1203,7 +1351,7 @@ static PHP_METHOD(swoole_socket_coro, connect) { swoole_get_socket_coro(sock, ZEND_THIS); - if (sock->socket->get_sock_domain() == AF_INET6 || sock->socket->get_sock_domain() == AF_INET) { + if (sock->socket->is_port_required()) { if (ZEND_NUM_ARGS() == 1) { php_swoole_error(E_WARNING, "Socket of type AF_INET/AF_INET6 requires port argument"); RETURN_FALSE; @@ -1214,7 +1362,7 @@ static PHP_METHOD(swoole_socket_coro, connect) { } Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_CONNECT); if (!sock->socket->connect(std::string(host, l_host), port)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1224,10 +1372,20 @@ static PHP_METHOD(swoole_socket_coro, checkLiveness) { swoole_get_socket_coro(sock, ZEND_THIS); bool liveness = sock->socket->check_liveness(); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_BOOL(liveness); } +static PHP_METHOD(swoole_socket_coro, getBoundCid) { + zend_long event; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(event) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + RETURN_LONG(sock->socket->get_bound_cid((swEventType) event)); +} + static PHP_METHOD(swoole_socket_coro, peek) { zend_long length = SW_BUFFER_SIZE_BIG; @@ -1244,7 +1402,7 @@ static PHP_METHOD(swoole_socket_coro, peek) { zend_string *buf = zend_string_alloc(length, 0); ssize_t bytes = sock->socket->peek(ZSTR_VAL(buf), length); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(bytes < 0)) { zend_string_free(buf); RETURN_FALSE; @@ -1263,7 +1421,7 @@ enum RecvMode { SOCKET_RECV_WITH_BUFFER, }; -static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) { +static inline void socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) { zend_long length = SW_BUFFER_SIZE_BIG; double timeout = 0; @@ -1299,7 +1457,7 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMod assert(0); break; } - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(bytes < 0)) { zend_string_free(buf); RETURN_FALSE; @@ -1312,19 +1470,19 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMod } static PHP_METHOD(swoole_socket_coro, recv) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); } static PHP_METHOD(swoole_socket_coro, recvAll) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL); } static PHP_METHOD(swoole_socket_coro, recvLine) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE); } static PHP_METHOD(swoole_socket_coro, recvWithBuffer) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER); } static PHP_METHOD(swoole_socket_coro, recvPacket) { @@ -1337,7 +1495,7 @@ static PHP_METHOD(swoole_socket_coro, recvPacket) { swoole_get_socket_coro(sock, ZEND_THIS); ssize_t retval = sock->socket->recv_packet(timeout); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (retval < 0) { RETURN_FALSE; } else if (retval == 0) { @@ -1354,7 +1512,7 @@ static PHP_METHOD(swoole_socket_coro, recvPacket) { } } -static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static sw_inline void socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) { char *data; size_t length; double timeout = 0; @@ -1369,7 +1527,7 @@ static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, cons Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_WRITE); ssize_t retval = all ? sock->socket->send_all(data, length) : sock->socket->send(data, length); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(retval < 0)) { RETURN_FALSE; } else { @@ -1378,10 +1536,10 @@ static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, cons } static PHP_METHOD(swoole_socket_coro, send) { - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } -static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static void socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { zval *ziov = nullptr; zval *zelement = nullptr; HashTable *vht; @@ -1398,14 +1556,14 @@ static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const swoole_get_socket_coro(sock, ZEND_THIS); ON_SCOPE_EXIT { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); }; vht = Z_ARRVAL_P(ziov); iovcnt = zend_array_count(vht); if (iovcnt > IOV_MAX) { - sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, IOV_MAX_ERROR_MSG, IOV_MAX); + sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX); sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string()); RETURN_FALSE; } @@ -1443,14 +1601,14 @@ static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const } static PHP_METHOD(swoole_socket_coro, writeVector) { - swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_socket_coro, writeVectorAll) { - swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } -static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static void socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { zval *ziov = nullptr; zval *zelement = nullptr; HashTable *vht; @@ -1468,14 +1626,14 @@ static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const b swoole_get_socket_coro(sock, ZEND_THIS); ON_SCOPE_EXIT { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); }; vht = Z_ARRVAL_P(ziov); iovcnt = zend_array_count(vht); if (iovcnt > IOV_MAX) { - sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, IOV_MAX_ERROR_MSG, IOV_MAX); + sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX); sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string()); RETURN_FALSE; } @@ -1551,11 +1709,11 @@ static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const b } static PHP_METHOD(swoole_socket_coro, readVector) { - swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_socket_coro, readVectorAll) { - swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_socket_coro, sendFile) { @@ -1564,9 +1722,13 @@ static PHP_METHOD(swoole_socket_coro, sendFile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &file, &file_len, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(file, file_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (file_len == 0) { php_swoole_fatal_error(E_WARNING, "file to send is empty"); RETURN_FALSE; @@ -1585,7 +1747,7 @@ static PHP_METHOD(swoole_socket_coro, sendFile) { } static PHP_METHOD(swoole_socket_coro, sendAll) { - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_socket_coro, recvfrom) { @@ -1603,7 +1765,7 @@ static PHP_METHOD(swoole_socket_coro, recvfrom) { zend_string *buf = zend_string_alloc(SW_BUFFER_SIZE_BIG, 0); Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_READ); ssize_t bytes = sock->socket->recvfrom(ZSTR_VAL(buf), SW_BUFFER_SIZE_BIG); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (bytes < 0) { zend_string_free(buf); RETURN_FALSE; @@ -1638,7 +1800,7 @@ static PHP_METHOD(swoole_socket_coro, sendto) { swoole_get_socket_coro(sock, ZEND_THIS); ssize_t retval = sock->socket->sendto(std::string(addr, l_addr), port, data, l_data); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (retval < 0) { RETURN_FALSE; } else { @@ -1657,7 +1819,7 @@ static PHP_METHOD(swoole_socket_coro, shutdown) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->shutdown(how)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1666,16 +1828,27 @@ static PHP_METHOD(swoole_socket_coro, shutdown) { static PHP_METHOD(swoole_socket_coro, close) { swoole_get_socket_coro(sock, ZEND_THIS); if (sock->reference) { + php_swoole_error(E_WARNING, "cannot close the referenced resource"); RETURN_FALSE; } - if (sock->socket->protocol.private_data) { - zend_fcall_info_cache *package_length_func = (zend_fcall_info_cache *) sock->socket->protocol.private_data; - sw_zend_fci_cache_discard(package_length_func); - efree(package_length_func); - } - if (sock->socket->close()) { - delete sock->socket; - sock->socket = SW_BAD_SOCKET; + if (sock->socket->protocol.private_data_1) { + sw_callable_free(sock->socket->protocol.private_data_1); + sock->socket->protocol.private_data_1 = nullptr; + } + if (!Z_ISUNDEF(sock->zstream)) { + php_stream *stream = NULL; + php_stream_from_zval_no_verify(stream, &sock->zstream); + if (stream != NULL) { + /* close & destroy stream, incl. removing it from the rsrc list; + * resource stored in php_sock->zstream will become invalid */ + php_stream_free(stream, + PHP_STREAM_FREE_KEEP_RSRC | PHP_STREAM_FREE_CLOSE | + (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : 0)); + } + ZVAL_UNDEF(&sock->zstream); + sock->socket->move_fd(); + } else { + sock->socket->close(); } RETURN_TRUE; } @@ -1685,7 +1858,7 @@ static PHP_METHOD(swoole_socket_coro, getsockname) { Address sa; if (!sock->socket->getsockname(&sa)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } @@ -1699,7 +1872,7 @@ static PHP_METHOD(swoole_socket_coro, getpeername) { Address sa; if (!sock->socket->getpeername(&sa)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } @@ -1714,9 +1887,10 @@ static PHP_METHOD(swoole_socket_coro, getOption) { int other_val; zend_long level, optname; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &level, &optname) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(level) + Z_PARAM_LONG(optname) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); swoole_get_socket_coro(sock, ZEND_THIS); auto _socket = sock->socket->get_socket(); @@ -1780,6 +1954,22 @@ static PHP_METHOD(swoole_socket_coro, getOption) { add_assoc_long(return_value, "usec", (timeout - (double) sec) * 1000000); break; } +#ifdef TCP_INFO + case TCP_INFO: { + tcp_info info; + socklen_t len = sizeof(info); + if (_socket->get_option(SOL_TCP, TCP_INFO, &info, &len) < 0) { + php_swoole_sys_error(E_WARNING, "getsockopt(%d, SOL_TCP, TCP_INFO)", sock->socket->get_fd()); + } else { + array_init(return_value); + auto info_map = sw_socket_parse_tcp_info(&info); + for (const auto &iter : info_map) { + add_assoc_long_ex(return_value, iter.first.c_str(), iter.first.length(), (zend_long) iter.second); + } + } + break; + } +#endif default: { optlen = sizeof(other_val); @@ -1814,9 +2004,11 @@ static PHP_METHOD(swoole_socket_coro, setOption) { zval *l_onoff, *l_linger; zval *sec, *usec; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &level, &optname, &arg4) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(level) + Z_PARAM_LONG(optname) + Z_PARAM_ZVAL(arg4) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); swoole_get_socket_coro(sock, ZEND_THIS); @@ -1962,3 +2154,67 @@ static PHP_METHOD(swoole_socket_coro, sslHandshake) { RETURN_BOOL(sock->socket->ssl_handshake()); } #endif + +static PHP_METHOD(swoole_socket_coro, isClosed) { + RETURN_BOOL(php_swoole_socket_is_closed(ZEND_THIS)); +} + +static PHP_METHOD(swoole_socket_coro, import) { + zval *zstream; + php_stream *stream; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(zstream) + ZEND_PARSE_PARAMETERS_END(); + + php_stream_from_zval(stream, zstream); + + enum swSocketType type = SW_SOCK_TCP; + int socket_fd; + + if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void **) &socket_fd, 1)) { + /* error supposedly already shown */ + RETURN_FALSE; + } + + int sock_domain = AF_INET, sock_type = SOCK_STREAM; + php_sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + +#ifdef SO_DOMAIN + socklen_t sock_domain_len = sizeof(sock_domain); + if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) == 0) { + } else +#endif + if (getsockname(socket_fd, (struct sockaddr *) &addr, &addr_len) == 0) { + sock_domain = addr.ss_family; + } else { + php_swoole_sys_error(E_WARNING, "getsockname() failed"); + RETURN_FALSE; + } + +#ifdef SO_TYPE + socklen_t sock_type_len = sizeof(sock_type); + if (getsockopt(socket_fd, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len) < 0) { + php_swoole_sys_error(E_WARNING, "getsockopt(SOL_SOCKET, SO_TYPE) failed"); + RETURN_FALSE; + } +#endif + + type = swoole::network::Socket::convert_to_type(sock_domain, sock_type); + + /* determine blocking mode */ + int t = fcntl(socket_fd, F_GETFL); + if (t < 0) { + php_swoole_sys_error(E_WARNING, "fcntl(F_GETFL) failed"); + RETURN_FALSE; + } + zend_object *object = php_swoole_create_socket_from_fd(socket_fd, type); + SocketObject *sock = socket_coro_fetch_object(object); + + ZVAL_COPY(&sock->zstream, zstream); + php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); + sock->socket->get_socket()->nonblock = (t & O_NONBLOCK); + + RETURN_OBJ(object); +} diff --git a/ext-src/swoole_sqlite.cc b/ext-src/swoole_sqlite.cc new file mode 100644 index 00000000000..0159ba3383f --- /dev/null +++ b/ext-src/swoole_sqlite.cc @@ -0,0 +1,118 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ +#include "php_swoole_private.h" +#include "php_swoole_cxx.h" +#include "swoole_coroutine.h" +#include "php_swoole_sqlite.h" + +#ifdef SW_USE_SQLITE +using swoole::Coroutine; + +static SW_THREAD_LOCAL bool swoole_sqlite_blocking = true; + +void swoole_sqlite_set_blocking(bool blocking) { + if (blocking) { + swoole_sqlite_blocking = blocking; + return; + } + + int thread_safe_mode = sqlite3_threadsafe(); + if (!thread_safe_mode) { + swoole_warning("hook sqlite coroutine failed because thread safe mode is single-thread."); + return; + } + swoole_sqlite_blocking = blocking; +} + +int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_open_v2"); + + if (!swoole_sqlite_blocking && Coroutine::get_current()) { + flags |= SQLITE_OPEN_FULLMUTEX; + } + + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_open_v2(filename, ppDb, flags, zVfs); }); + + return result; +} + +int swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_prepare_v2"); + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail); }); + + return result; +} + +int swoole_sqlite3_exec( + sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *argument, char **errmsg) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_exec"); + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_exec(db, sql, callback, argument, errmsg); }); + + return result; +} + +int swoole_sqlite3_close(sqlite3 *db) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close"); + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close(db); }); + + return result; +} + +int swoole_sqlite3_close_v2(sqlite3 *db) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close_v2"); + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close_v2(db); }); + + return result; +} + +int swoole_sqlite3_step(sqlite3_stmt *stmt) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_step"); + int result = 0; + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_step(stmt); }); + + return result; +} + +void php_swoole_sqlite_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("SQLITE_ATTR_OPEN_FLAGS")) == nullptr) { +#ifdef SQLITE_DETERMINISTIC + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_DETERMINISTIC", (zend_long) SQLITE_DETERMINISTIC); +#endif + + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_OPEN_FLAGS", (zend_long) PDO_SQLITE_ATTR_OPEN_FLAGS); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READONLY", (zend_long) SQLITE_OPEN_READONLY); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long) SQLITE_OPEN_READWRITE); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long) SQLITE_OPEN_CREATE); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long) PDO_SQLITE_ATTR_READONLY_STATEMENT); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", + (zend_long) PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + } + + php_pdo_unregister_driver(&swoole_pdo_sqlite_driver); + php_pdo_register_driver(&swoole_pdo_sqlite_driver); +} + +void php_swoole_sqlite_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_sqlite_driver); +} +#endif diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 5b55b80808f..0eab1792796 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,6 +18,10 @@ #include "swoole_table.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_table_arginfo.h" +END_EXTERN_C() + using namespace swoole; static inline void php_swoole_table_row2array(Table *table, TableRow *row, zval *return_value) { @@ -87,8 +91,8 @@ static inline Table *php_swoole_table_get_ptr(zval *zobject) { static inline Table *php_swoole_table_get_and_check_ptr(zval *zobject) { Table *table = php_swoole_table_get_ptr(zobject); - if (!table) { - php_swoole_fatal_error(E_ERROR, "you must call Table constructor first"); + if (UNEXPECTED(!table)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return table; } @@ -106,10 +110,6 @@ static void inline php_swoole_table_set_ptr(zval *zobject, Table *ptr) { } static inline void php_swoole_table_free_object(zend_object *object) { - Table *table = php_swoole_table_fetch_object(object)->ptr; - if (table) { - table->free(); - } zend_object_std_dtor(object); } @@ -121,52 +121,6 @@ static inline zend_object *php_swoole_table_create_object(zend_class_entry *ce) return &table->std; } -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_construct, 0, 0, 1) - ZEND_ARG_INFO(0, table_size) - ZEND_ARG_INFO(0, conflict_proportion) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_column, 0, 0, 2) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, value, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, field) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_exists, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_del, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_incr, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, column) - ZEND_ARG_INFO(0, incrby) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_decr, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, column) - ZEND_ARG_INFO(0, decrby) -ZEND_END_ARG_INFO() -// clang-format on - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_table, __construct); static PHP_METHOD(swoole_table, column); @@ -194,34 +148,34 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_table_methods[] = { - PHP_ME(swoole_table, __construct, arginfo_swoole_table_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, column, arginfo_swoole_table_column, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, create, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, destroy, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, set, arginfo_swoole_table_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, get, arginfo_swoole_table_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, count, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_table, delete, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, exists, arginfo_swoole_table_exists, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_table, exist, exists, arginfo_swoole_table_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, incr, arginfo_swoole_table_incr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, decr, arginfo_swoole_table_decr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, getSize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, getMemorySize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, stats, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, __construct, arginfo_class_Swoole_Table___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, column, arginfo_class_Swoole_Table_column, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, create, arginfo_class_Swoole_Table_create, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, destroy, arginfo_class_Swoole_Table_destroy, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, set, arginfo_class_Swoole_Table_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, get, arginfo_class_Swoole_Table_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, count, arginfo_class_Swoole_Table_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, del, arginfo_class_Swoole_Table_del, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_table, delete, del, arginfo_class_Swoole_Table_del, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, exists, arginfo_class_Swoole_Table_exists, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_table, exist, exists, arginfo_class_Swoole_Table_exists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, incr, arginfo_class_Swoole_Table_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, decr, arginfo_class_Swoole_Table_decr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, getSize, arginfo_class_Swoole_Table_getSize, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, getMemorySize, arginfo_class_Swoole_Table_getMemorySize, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, stats, arginfo_class_Swoole_Table_stats, ZEND_ACC_PUBLIC) // implement Iterator - PHP_ME(swoole_table, rewind, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, valid, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, next, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, current, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, key, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, rewind, arginfo_class_Swoole_Table_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, valid, arginfo_class_Swoole_Table_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, next, arginfo_class_Swoole_Table_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, current, arginfo_class_Swoole_Table_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, key, arginfo_class_Swoole_Table_key, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_table_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_table, "Swoole\\Table", "swoole_table", nullptr, swoole_table_methods); + SW_INIT_CLASS_ENTRY(swoole_table, "Swoole\\Table", nullptr, swoole_table_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_table); SW_SET_CLASS_CLONEABLE(swoole_table, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_table, sw_zend_class_unset_property_deny); @@ -243,7 +197,8 @@ void php_swoole_table_minit(int module_number) { PHP_METHOD(swoole_table, __construct) { Table *table = php_swoole_table_get_ptr(ZEND_THIS); if (table) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zend_long table_size; @@ -298,7 +253,8 @@ static PHP_METHOD(swoole_table, create) { RETURN_FALSE; } zend_update_property_long(swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("size"), table->get_size()); - zend_update_property_long(swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("memorySize"), table->get_memory_size()); + zend_update_property_long( + swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("memorySize"), table->get_memory_size()); RETURN_TRUE; } @@ -346,7 +302,7 @@ static PHP_METHOD(swoole_table, set) { TableColumn *col = *i; zval *zv = zend_hash_str_find(ht, col->name.c_str(), col->name.length()); if (zv == nullptr || ZVAL_IS_NULL(zv)) { - col->clear(row); + col->clear(row); } else { if (col->type == TableColumn::TYPE_STRING) { zend_string *str = zval_get_string(zv); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc new file mode 100644 index 00000000000..1767d25a165 --- /dev/null +++ b/ext-src/swoole_thread.cc @@ -0,0 +1,1199 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" + +#ifdef SW_THREAD + +#include +#include + +#include +#include + +#include "swoole_thread.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_arginfo.h" +END_EXTERN_C() + +zend_class_entry *swoole_thread_ce; +static zend_object_handlers swoole_thread_handlers; + +zend_class_entry *swoole_thread_error_ce; +static zend_object_handlers swoole_thread_error_handlers; + +static struct { + char *path_translated; + zend_string *argv_serialized; + int argc; +} request_info; + +TSRMLS_CACHE_EXTERN(); + +typedef std::thread Thread; + +struct ThreadObject { + Thread *thread; + zend_object std; +}; + +static void thread_register_stdio_file_handles(bool no_close); + +static thread_local zval thread_argv = {}; +static thread_local JMP_BUF *thread_bailout = nullptr; +static std::atomic thread_num(1); +static zend::ConcurrencyHashMap thread_exit_status(-1); + +static sw_inline ThreadObject *thread_fetch_object(zend_object *obj) { + return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); +} + +static pthread_t thread_get_id(zend_object *object) { + zval *res, rv; + res = zend_read_property(swoole_thread_ce, object, ZEND_STRL("id"), 1, &rv); + return (pthread_t) zval_get_long(res); +} + +static pthread_t thread_get_id(zval *zobj) { + return thread_get_id(Z_OBJ_P(zobj)); +} + +static void thread_join(zend_object *object) { + ThreadObject *to = thread_fetch_object(object); + if (to->thread && to->thread->joinable()) { + to->thread->join(); + php_swoole_thread_join(to->thread->native_handle()); + delete to->thread; + to->thread = nullptr; + } +} + +static void thread_free_object(zend_object *object) { + thread_join(object); + zend_object_std_dtor(object); +} + +static zend_object *thread_create_object(zend_class_entry *ce) { + ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); + zend_object_std_init(&to->std, ce); + object_properties_init(&to->std, ce); + to->std.handlers = &swoole_thread_handlers; + return &to->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread, __construct); +static PHP_METHOD(swoole_thread, join); +static PHP_METHOD(swoole_thread, joinable); +static PHP_METHOD(swoole_thread, getExitStatus); +static PHP_METHOD(swoole_thread, detach); +static PHP_METHOD(swoole_thread, getArguments); +static PHP_METHOD(swoole_thread, getId); +static PHP_METHOD(swoole_thread, getInfo); +static PHP_METHOD(swoole_thread, setName); +#ifdef HAVE_CPU_AFFINITY +static PHP_METHOD(swoole_thread, setAffinity); +static PHP_METHOD(swoole_thread, getAffinity); +#endif +static PHP_METHOD(swoole_thread, setPriority); +static PHP_METHOD(swoole_thread, getPriority); +static PHP_METHOD(swoole_thread, getNativeId); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_methods[] = { + PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, getExitStatus, arginfo_class_Swoole_Thread_getExitStatus, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getInfo, arginfo_class_Swoole_Thread_getInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, setName, arginfo_class_Swoole_Thread_setName, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef HAVE_CPU_AFFINITY + PHP_ME(swoole_thread, setAffinity, arginfo_class_Swoole_Thread_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getAffinity, arginfo_class_Swoole_Thread_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#endif + PHP_ME(swoole_thread, setPriority, arginfo_class_Swoole_Thread_setPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getPriority, arginfo_class_Swoole_Thread_getPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getNativeId, arginfo_class_Swoole_Thread_getNativeId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread, "Swoole\\Thread", nullptr, swoole_thread_methods); + swoole_thread_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread, thread_create_object, thread_free_object, ThreadObject, std); + + zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_class_constant_long( + swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); + zend_declare_class_constant_string(swoole_thread_ce, ZEND_STRL("API_NAME"), tsrm_api_name()); + + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_OTHER"), SCHED_OTHER); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_FIFO"), SCHED_FIFO); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_RR"), SCHED_RR); +#ifdef SCHED_BATCH + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_BATCH"), SCHED_BATCH); +#endif +#ifdef SCHED_ISO + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_ISO"), SCHED_ISO); +#endif +#ifdef SCHED_IDLE + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_IDLE"), SCHED_IDLE); +#endif +#ifdef SCHED_DEADLINE + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_DEADLINE"), SCHED_DEADLINE); +#endif + + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, "Swoole\\Thread\\Error"); + zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL("code"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +} + +static PHP_METHOD(swoole_thread, __construct) { + char *script_file; + size_t l_script_file; + zval *args; + int argc; + ZendArray *argv = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STRING(script_file, l_script_file) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (l_script_file < 1) { + zend_throw_exception(swoole_exception_ce, "exec file name is empty", SW_ERROR_INVALID_PARAMS); + return; + } + + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_string *file = zend_string_init(script_file, l_script_file, 1); + + if (argc > 0) { + argv = new ZendArray(); + for (int i = 0; i < argc; i++) { + argv->append(&args[i]); + } + } + + try { + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + } catch (const std::exception &e) { + zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL); + return; + } + zend_update_property_long( + swoole_thread_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), (zend_long) to->thread->native_handle()); +} + +static PHP_METHOD(swoole_thread, join) { + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (!to || !to->thread || !to->thread->joinable()) { + RETURN_FALSE; + } + thread_join(Z_OBJ_P(ZEND_THIS)); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, joinable) { + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + RETURN_BOOL(to->thread->joinable()); +} + +static PHP_METHOD(swoole_thread, detach) { + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + to->thread->detach(); + delete to->thread; + to->thread = nullptr; + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, getArguments) { + if (Z_TYPE(thread_argv) == IS_ARRAY) { + RETURN_ZVAL(&thread_argv, 1, 0); + } +} + +static PHP_METHOD(swoole_thread, getId) { + RETURN_LONG((zend_long) pthread_self()); +} + +static PHP_METHOD(swoole_thread, getExitStatus) { + RETURN_LONG(php_swoole_thread_get_exit_status(thread_get_id(ZEND_THIS))); +} + +static PHP_METHOD(swoole_thread, setName) { + char *name; + size_t l_name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(name, l_name) + ZEND_PARSE_PARAMETERS_END(); + + RETURN_BOOL(swoole_thread_set_name(name)); +} + +#ifdef HAVE_CPU_AFFINITY +static PHP_METHOD(swoole_thread, setAffinity) { + zval *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END(); + + cpu_set_t cpu_set; + if (!php_swoole_array_to_cpu_set(array, &cpu_set)) { + RETURN_FALSE; + } + + if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) { + php_swoole_error(E_WARNING, "pthread_setaffinity_np() failed"); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, getAffinity) { + cpu_set_t cpu_set; + if (pthread_getaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) { + php_swoole_error(E_WARNING, "pthread_getaffinity_np() failed"); + RETURN_FALSE; + } + php_swoole_cpu_set_to_array(return_value, &cpu_set); +} +#endif + +static PHP_METHOD(swoole_thread, setPriority) { + zend_long priority, policy = -1; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(priority) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(policy) + ZEND_PARSE_PARAMETERS_END(); + + struct sched_param param; + if (policy == -1) { + pthread_setschedparam(pthread_self(), policy, ¶m); + } + + param.sched_priority = priority; + int retval = pthread_setschedparam(pthread_self(), policy, ¶m); + if (retval == 0) { + RETURN_TRUE; + } else { + php_swoole_sys_error(E_WARNING, "pthread_setschedparam() failed"); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_thread, getPriority) { + struct sched_param param; + int policy; + if (pthread_getschedparam(pthread_self(), &policy, ¶m) != 0) { + php_swoole_error(E_WARNING, "pthread_getschedparam() failed"); + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("policy"), policy); + add_assoc_long_ex(return_value, ZEND_STRL("priority"), param.sched_priority); +} + +static PHP_METHOD(swoole_thread, getNativeId) { + RETURN_LONG((zend_long) swoole_thread_get_native_id()); +} + +void php_swoole_thread_rinit() { + if (tsrm_is_main_thread()) { + if (SG(request_info).path_translated) { + request_info.path_translated = strdup(SG(request_info).path_translated); + } + // Return reference + zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); + if (global_argv) { + request_info.argv_serialized = php_swoole_serialize(global_argv); + request_info.argc = SG(request_info).argc; + } + } +} + +void php_swoole_thread_rshutdown() { + zval_dtor(&thread_argv); + if (!tsrm_is_main_thread()) { + return; + } + if (sw_active_thread_count() > 1) { + swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", sw_active_thread_count()); + exit(200); + } + if (request_info.path_translated) { + free((void *) request_info.path_translated); + request_info.path_translated = nullptr; + } + if (request_info.argv_serialized) { + zend_string_release(request_info.argv_serialized); + request_info.argv_serialized = nullptr; + } +} + +static void thread_register_stdio_file_handles(bool no_close) { + php_stream *s_in, *s_out, *s_err; + php_stream_context *sc_in = NULL, *sc_out = NULL, *sc_err = NULL; + zend_constant ic, oc, ec; + + s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); + s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); + s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); + + if (s_in == NULL || s_out == NULL || s_err == NULL) { + if (s_in) php_stream_close(s_in); + if (s_out) php_stream_close(s_out); + if (s_err) php_stream_close(s_err); + return; + } + + if (no_close) { + s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; + } + + php_stream_to_zval(s_in, &ic.value); + php_stream_to_zval(s_out, &oc.value); + php_stream_to_zval(s_err, &ec.value); + + ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0); + ic.name = zend_string_init_interned("STDIN", sizeof("STDIN") - 1, 0); + zend_register_constant(&ic); + + ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0); + oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT") - 1, 0); + zend_register_constant(&oc); + + ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0); + ec.name = zend_string_init_interned("STDERR", sizeof("STDERR") - 1, 0); + zend_register_constant(&ec); +} + +void php_swoole_thread_start(zend_string *file, ZendArray *argv) { + thread_num.fetch_add(1); + ts_resource(0); +#if defined(COMPILE_DL_SWOOLE) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + zend_file_handle file_handle{}; + zval global_argc, global_argv; + + PG(expose_php) = 0; + PG(auto_globals_jit) = 1; +#if PHP_VERSION_ID >= 80100 + PG(enable_dl) = false; +#else + PG(enable_dl) = 0; +#endif + + swoole_thread_init(); + + if (php_request_startup() != SUCCESS) { + EG(exit_status) = 1; + goto _startup_error; + } + + PG(during_request_startup) = 0; + SG(sapi_started) = 0; + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + SG(request_info).path_translated = request_info.path_translated; + SG(request_info).argc = request_info.argc; + + zend_stream_init_filename(&file_handle, ZSTR_VAL(file)); + file_handle.primary_script = 1; + + zend_first_try { + thread_bailout = EG(bailout); + if (request_info.argv_serialized) { + php_swoole_unserialize(request_info.argv_serialized, &global_argv); + ZVAL_LONG(&global_argc, request_info.argc); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); + } + if (argv) { + argv->to_array(&thread_argv); + argv->del_ref(); + } + thread_register_stdio_file_handles(true); + php_execute_script(&file_handle); + } + zend_end_try(); + + zend_destroy_file_handle(&file_handle); + + php_request_shutdown(NULL); + file_handle.filename = NULL; + +_startup_error: + thread_exit_status.set(pthread_self(), EG(exit_status)); + + zend_string_release(file); + ts_free_thread(); + swoole_thread_clean(); + thread_num.fetch_sub(1); +} + +void php_swoole_thread_join(pthread_t ptid) { + thread_exit_status.del(ptid); +} + +int php_swoole_thread_get_exit_status(pthread_t ptid) { + return thread_exit_status.get(ptid); +} + +size_t sw_active_thread_count(void) { + return thread_num.load(); +} + +void php_swoole_thread_bailout(void) { + if (thread_bailout) { + EG(bailout) = thread_bailout; + } + zend_bailout(); +} + +int php_swoole_thread_stream_cast(zval *zstream) { + php_stream *stream; + int sockfd; + int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; + if ((php_stream_from_zval_no_verify(stream, zstream))) { + if (php_stream_cast(stream, cast_flags, (void **) &sockfd, 1) == SUCCESS && sockfd >= 0) { + return dup(sockfd); + } + } + return -1; +} + +int php_swoole_thread_co_socket_cast(zval *zvalue, swSocketType *type) { + swoole::coroutine::Socket *socket = php_swoole_get_socket(zvalue); + if (!socket) { + return -1; + } + int sockfd = socket->get_fd(); + if (sockfd < 0) { + return -1; + } + int newfd = dup(sockfd); + if (newfd < 0) { + return -1; + } + *type = socket->get_type(); + return newfd; +} + +void php_swoole_thread_stream_create(zval *return_value, zend_long sockfd) { + std::string path = "php://fd/" + std::to_string(sockfd); + // The file descriptor will be duplicated once here + php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); + if (stream) { + php_stream_to_zval(stream, return_value); + } else { + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); + } +} + +void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type) { + int newfd = dup(sockfd); + if (newfd < 0) { + _error: + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); + return; + } + zend_object *sockobj = php_swoole_create_socket_from_fd(newfd, type); + if (sockobj) { + ZVAL_OBJ(return_value, sockobj); + } else { + goto _error; + } +} + +#ifdef SWOOLE_SOCKETS_SUPPORT +void php_swoole_thread_php_socket_create(zval *return_value, zend_long sockfd) { + int newfd = dup(sockfd); + if (newfd < 0) { + _error: + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); + return; + } + object_init_ex(return_value, socket_ce); + auto retsock = Z_SOCKET_P(return_value); + if (!socket_import_file_descriptor(newfd, retsock)) { + goto _error; + } +} +#endif + +static PHP_METHOD(swoole_thread, getInfo) { + array_init(return_value); + add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); + add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); + add_assoc_long(return_value, "thread_num", thread_num.load()); +} + +#define CAST_OBJ_TO_RESOURCE(_name, _type) \ + else if (instanceof_function(Z_OBJCE_P(zvalue), swoole_thread_##_name##_ce)) { \ + value.resource = php_swoole_thread_##_name##_cast(zvalue); \ + value.resource->add_ref(); \ + type = _type; \ + break; \ + } + +void ArrayItem::store(zval *zvalue) { + type = Z_TYPE_P(zvalue); + switch (type) { + case IS_LONG: + value.lval = zval_get_long(zvalue); + break; + case IS_DOUBLE: + value.dval = zval_get_double(zvalue); + break; + case IS_STRING: { + value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); + break; + } + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + break; + case IS_RESOURCE: { + value.socket.fd = php_swoole_thread_stream_cast(zvalue); + type = IS_STREAM_SOCKET; + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to convert to socket fd", errno); + } + break; + } + case IS_ARRAY: { + type = zend_array_is_list(Z_ARRVAL_P(zvalue)) ? IS_ARRAYLIST : IS_MAP; + value.resource = ZendArray::from(Z_ARRVAL_P(zvalue)); + break; + } + case IS_OBJECT: { + if (sw_zval_is_co_socket(zvalue)) { + value.socket.fd = php_swoole_thread_co_socket_cast(zvalue, &value.socket.type); + type = IS_CO_SOCKET; + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to convert to socket fd", errno); + } + break; + } +#ifdef SWOOLE_SOCKETS_SUPPORT + else if (sw_zval_is_php_socket(zvalue)) { + php_socket *php_sock = SW_Z_SOCKET_P(zvalue); + if (php_sock->bsd_socket == -1) { + zend_throw_exception(swoole_exception_ce, "invalid socket fd", EBADF); + break; + } + value.socket.fd = dup(php_sock->bsd_socket); + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to dup socket fd", errno); + } + type = IS_PHP_SOCKET; + break; + } +#endif + CAST_OBJ_TO_RESOURCE(arraylist, IS_ARRAYLIST) + CAST_OBJ_TO_RESOURCE(map, IS_MAP) + CAST_OBJ_TO_RESOURCE(queue, IS_QUEUE) + CAST_OBJ_TO_RESOURCE(lock, IS_LOCK) + CAST_OBJ_TO_RESOURCE(atomic, IS_ATOMIC) + CAST_OBJ_TO_RESOURCE(atomic_long, IS_ATOMIC_LONG) + CAST_OBJ_TO_RESOURCE(barrier, IS_BARRIER) + } + /* no break */ + default: { + auto _serialized_object = php_swoole_serialize(zvalue); + if (!_serialized_object) { + type = IS_UNDEF; + break; + } else { + type = IS_SERIALIZED_OBJECT; + value.serialized_object = _serialized_object; + } + break; + } + } +} + +bool ArrayItem::equals(zval *zvalue) { + if (Z_TYPE_P(zvalue) != type) { + return false; + } + switch (type) { + case IS_LONG: + return Z_LVAL_P(zvalue) == value.lval; + case IS_DOUBLE: + return Z_DVAL_P(zvalue) == value.dval; + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + return true; + case IS_STRING: + return zend_string_equals(value.str, Z_STR_P(zvalue)); + default: + return false; + } +} + +#define TYPE_PAIR(t1, t2) (((t1) << 4) | (t2)) +#define ITEM_TYPE(item) (item->type) +#define ITEM_LVAL(item) (item->value.lval) +#define ITEM_DVAL(item) (item->value.dval) +#define ITEM_STR(item) (item->value.str) + +static int compare_long_to_string(zend_long lval, zend_string *str) /* {{{ */ +{ + zend_long str_lval; + double str_dval; + zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0); + + if (type == IS_LONG) { + return lval > str_lval ? 1 : lval < str_lval ? -1 : 0; + } + + if (type == IS_DOUBLE) { + double diff = (double) lval - str_dval; + return ZEND_NORMALIZE_BOOL(diff); + } + + zend_string *lval_as_str = zend_long_to_str(lval); + int cmp_result = zend_binary_strcmp(ZSTR_VAL(lval_as_str), ZSTR_LEN(lval_as_str), ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(lval_as_str); + return ZEND_NORMALIZE_BOOL(cmp_result); +} +/* }}} */ + +static int compare_double_to_string(double dval, zend_string *str) /* {{{ */ +{ + zend_long str_lval; + double str_dval; + zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0); + + if (type == IS_LONG) { + double diff = dval - (double) str_lval; + return ZEND_NORMALIZE_BOOL(diff); + } + + if (type == IS_DOUBLE) { + if (dval == str_dval) { + return 0; + } + return ZEND_NORMALIZE_BOOL(dval - str_dval); + } + + zend_string *dval_as_str = zend_double_to_str(dval); + int cmp_result = zend_binary_strcmp(ZSTR_VAL(dval_as_str), ZSTR_LEN(dval_as_str), ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(dval_as_str); + return ZEND_NORMALIZE_BOOL(cmp_result); +} +/* }}} */ + +int ArrayItem::compare(Bucket *a, Bucket *b) { + ArrayItem *op1 = static_cast(Z_PTR(a->val)); + ArrayItem *op2 = static_cast(Z_PTR(b->val)); + + switch (TYPE_PAIR(ITEM_TYPE(op1), ITEM_TYPE(op2))) { + case TYPE_PAIR(IS_LONG, IS_LONG): + return ITEM_LVAL(op1) > ITEM_LVAL(op2) ? 1 : (ITEM_LVAL(op1) < ITEM_LVAL(op2) ? -1 : 0); + + case TYPE_PAIR(IS_DOUBLE, IS_LONG): + return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - (double) ITEM_LVAL(op2)); + + case TYPE_PAIR(IS_LONG, IS_DOUBLE): + return ZEND_NORMALIZE_BOOL((double) ITEM_LVAL(op1) - ITEM_DVAL(op2)); + + case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE): + if (ITEM_DVAL(op1) == ITEM_DVAL(op2)) { + return 0; + } else { + return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - ITEM_DVAL(op2)); + } + + case TYPE_PAIR(IS_NULL, IS_NULL): + case TYPE_PAIR(IS_NULL, IS_FALSE): + case TYPE_PAIR(IS_FALSE, IS_NULL): + case TYPE_PAIR(IS_FALSE, IS_FALSE): + case TYPE_PAIR(IS_TRUE, IS_TRUE): + return 0; + + case TYPE_PAIR(IS_NULL, IS_TRUE): + return -1; + + case TYPE_PAIR(IS_TRUE, IS_NULL): + return 1; + + case TYPE_PAIR(IS_STRING, IS_STRING): + if (ITEM_STR(op1) == ITEM_STR(op2)) { + return 0; + } + return zendi_smart_strcmp(ITEM_STR(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_NULL, IS_STRING): + return Z_STRLEN_P(op2) == 0 ? 0 : -1; + + case TYPE_PAIR(IS_STRING, IS_NULL): + return Z_STRLEN_P(op1) == 0 ? 0 : 1; + + case TYPE_PAIR(IS_LONG, IS_STRING): + return compare_long_to_string(ITEM_LVAL(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_STRING, IS_LONG): + return -compare_long_to_string(ITEM_LVAL(op2), ITEM_STR(op1)); + + case TYPE_PAIR(IS_DOUBLE, IS_STRING): + if (zend_isnan(ITEM_DVAL(op1))) { + return 1; + } + return compare_double_to_string(ITEM_DVAL(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_STRING, IS_DOUBLE): + if (zend_isnan(ITEM_DVAL(op2))) { + return 1; + } + return -compare_double_to_string(ITEM_DVAL(op2), ITEM_STR(op1)); + + case TYPE_PAIR(IS_OBJECT, IS_NULL): + return 1; + + case TYPE_PAIR(IS_NULL, IS_OBJECT): + return -1; + + default: + zend_throw_error(NULL, "Unsupported operand types"); + return 1; + } +} + +void ArrayItem::fetch(zval *return_value) { + switch (type) { + case IS_LONG: + RETVAL_LONG(value.lval); + break; + case IS_DOUBLE: + RETVAL_DOUBLE(value.dval); + break; + case IS_TRUE: + RETVAL_TRUE; + break; + case IS_FALSE: + RETVAL_FALSE; + break; + case IS_STRING: + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + break; + case IS_ARRAYLIST: + value.resource->add_ref(); + php_swoole_thread_arraylist_create(return_value, value.resource); + break; + case IS_QUEUE: + value.resource->add_ref(); + php_swoole_thread_queue_create(return_value, value.resource); + break; + case IS_LOCK: + value.resource->add_ref(); + php_swoole_thread_lock_create(return_value, value.resource); + break; + case IS_MAP: + value.resource->add_ref(); + php_swoole_thread_map_create(return_value, value.resource); + break; + case IS_BARRIER: + value.resource->add_ref(); + php_swoole_thread_barrier_create(return_value, value.resource); + break; + case IS_ATOMIC: + value.resource->add_ref(); + php_swoole_thread_atomic_create(return_value, value.resource); + break; + case IS_ATOMIC_LONG: + value.resource->add_ref(); + php_swoole_thread_atomic_long_create(return_value, value.resource); + break; + case IS_STREAM_SOCKET: + php_swoole_thread_stream_create(return_value, value.socket.fd); + break; + case IS_CO_SOCKET: + php_swoole_thread_co_socket_create(return_value, value.socket.fd, value.socket.type); + break; +#ifdef SWOOLE_SOCKETS_SUPPORT + case IS_PHP_SOCKET: + php_swoole_thread_php_socket_create(return_value, value.socket.fd); + break; +#endif + case IS_SERIALIZED_OBJECT: + php_swoole_unserialize(value.serialized_object, return_value); + break; + default: + break; + } +} + +void ArrayItem::release() { + if (type == IS_STRING) { + zend_string_release(value.str); + value.str = nullptr; + } else if (type == IS_STREAM_SOCKET || type == IS_CO_SOCKET || type == IS_PHP_SOCKET) { + close(value.socket.fd); + value.socket.fd = -1; + } else if (type == IS_SERIALIZED_OBJECT) { + zend_string_release(value.serialized_object); + value.serialized_object = nullptr; + } else if (type >= IS_ARRAYLIST && type <= IS_ATOMIC_LONG) { + value.resource->del_ref(); + value.resource = nullptr; + } +} + +#define INIT_DECR_VALUE(v) \ + zval rvalue = *v; \ + if (Z_TYPE_P(v) == IS_DOUBLE) { \ + rvalue.value.dval = -rvalue.value.dval; \ + } else { \ + ZVAL_LONG(&rvalue, -zval_get_long(v)); \ + } + +void ZendArray::incr_update(ArrayItem *item, zval *zvalue, zval *return_value) { + if (item->type == IS_DOUBLE) { + item->value.dval += zval_get_double(zvalue); + RETVAL_DOUBLE(item->value.dval); + } else { + item->value.lval += zval_get_long(zvalue); + RETVAL_LONG(item->value.lval); + } +} + +ArrayItem *ZendArray::incr_create(zval *zvalue, zval *return_value) { + zval rvalue = *zvalue; + if (Z_TYPE_P(zvalue) == IS_DOUBLE) { + RETVAL_DOUBLE(rvalue.value.dval); + } else { + ZVAL_LONG(&rvalue, zval_get_long(zvalue)); + RETVAL_LONG(rvalue.value.lval); + } + return new ArrayItem(&rvalue); +} + +void ZendArray::strkey_incr(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + ArrayItem *item; + + lock_.lock(); + item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + incr_update(item, zvalue, return_value); + } else { + item = incr_create(zvalue, return_value); + item->setKey(skey); + zend_hash_update_ptr(&ht, item->key, item); + } + lock_.unlock(); +} + +void ZendArray::intkey_incr(zend_long index, zval *zvalue, zval *return_value) { + ArrayItem *item; + lock_.lock(); + item = (ArrayItem *) (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + incr_update(item, zvalue, return_value); + } else { + item = incr_create(zvalue, return_value); + item = new ArrayItem(zvalue); + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); +} + +void ZendArray::strkey_decr(zval *zkey, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + strkey_incr(zkey, &rvalue, return_value); +} + +void ZendArray::intkey_decr(zend_long index, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + intkey_incr(index, &rvalue, return_value); +} + +void ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + lock_.lock(); + if (strkey_exists(skey)) { + RETVAL_FALSE; + } else { + add(skey, zvalue); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::intkey_add(zend_long index, zval *zvalue, zval *return_value) { + lock_.lock(); + if (intkey_exists(index)) { + RETVAL_FALSE; + } else { + add(index, zvalue); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::strkey_update(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + lock_.lock(); + if (!strkey_exists(skey)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_update_ptr(&ht, item->key, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::intkey_update(zend_long index, zval *zvalue, zval *return_value) { + lock_.lock(); + if (!intkey_exists(index)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + zend_hash_index_update_ptr(&ht, index, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +bool ZendArray::index_offsetGet(zend_long index, zval *return_value) { + bool out_of_range = true; + lock_.lock_rd(); + if (index_exists(index)) { + out_of_range = false; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + } + lock_.unlock(); + return !out_of_range; +} + +bool ZendArray::index_offsetSet(zend_long index, zval *zvalue) { + auto item = new ArrayItem(zvalue); + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + delete item; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + zend_hash_next_index_insert_ptr(&ht, item); + } else { + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); + return success; +} + +void ZendArray::append(zval *zvalue) { + zend_hash_next_index_insert_ptr(&ht, new ArrayItem(zvalue)); +} + +bool ZendArray::index_incr(zval *zkey, zval *zvalue, zval *return_value) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + auto item = incr_create(zvalue, return_value); + zend_hash_next_index_insert_ptr(&ht, item); + } else { + auto item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + incr_update(item, zvalue, return_value); + } + lock_.unlock(); + return success; +} + +void ZendArray::index_offsetExists(zend_long index, zval *return_value) { + lock_.lock_rd(); + RETVAL_BOOL(index_exists(index)); + lock_.unlock(); +} + +void ZendArray::index_offsetUnset(zend_long index) { + lock_.lock(); + zend_long i = index; + zend_long n = zend_hash_num_elements(&ht); + HT_FLAGS(&ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + delete item; + while (i < n - 1) { +#if PHP_VERSION_ID >= 80200 + Z_PTR(ht.arPacked[i]) = Z_PTR(ht.arPacked[i + 1]); +#else + Z_PTR(ht.arData[i].val) = Z_PTR(ht.arData[i + 1].val); +#endif + i++; + } + ht.nNumUsed--; + ht.nNumOfElements--; + ht.nNextFreeElement--; + lock_.unlock(); +} + +bool ZendArray::index_decr(zval *zkey, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + return index_incr(zkey, &rvalue, return_value); +} + +void ZendArray::keys(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); + zend_ulong num_idx; + zend_string *str_idx; + zval *entry; + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zend_ulong lval = 0; + + for (; lval < elem_count; ++lval) { + ZEND_HASH_FILL_SET_LONG(lval); + ZEND_HASH_FILL_NEXT(); + } + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { + if (str_idx) { + ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); + } else { + ZEND_HASH_FILL_SET_LONG(num_idx); + } + ZEND_HASH_FILL_NEXT(); + } + ZEND_HASH_FOREACH_END(); + } + (void) entry; + } + ZEND_HASH_FILL_END(); + lock_.unlock(); +} + +void ZendArray::values(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + void *tmp; + ZEND_HASH_FOREACH_PTR(&ht, tmp) { + zval value; + ArrayItem *item = (ArrayItem *) tmp; + item->fetch(&value); + zend_hash_next_index_insert_new(Z_ARR_P(return_value), &value); + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + +void ZendArray::to_array(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_string *key; + zend_ulong index; + void *tmp; + ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) { + zval value; + ArrayItem *item = (ArrayItem *) tmp; + item->fetch(&value); + if (key) { + zend_hash_str_add(Z_ARR_P(return_value), ZSTR_VAL(key), ZSTR_LEN(key), &value); + } else { + zend_hash_index_add(Z_ARR_P(return_value), index, &value); + } + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + +void ZendArray::find(zval *search, zval *return_value) { + lock_.lock_rd(); + zend_string *key; + zend_ulong index; + void *tmp; + ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) { + ArrayItem *item = (ArrayItem *) tmp; + if (item->equals(search)) { + if (key) { + RETVAL_STRINGL(ZSTR_VAL(key), ZSTR_LEN(key)); + } else { + RETVAL_LONG(index); + } + break; + } + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + +void ZendArray::sort(bool renumber) { + lock_.lock(); + zend_hash_sort(&ht, ArrayItem::compare, renumber); + lock_.unlock(); +} + +ZendArray *ZendArray::from(zend_array *src) { + zend_string *key; + zend_ulong index; + zval *tmp; + ZendArray *result = new ZendArray(); + ZEND_HASH_FOREACH_KEY_VAL(src, index, key, tmp) { + ZVAL_DEREF(tmp); + if (key) { + result->add(key, tmp); + } else { + result->add(index, tmp); + } + } + ZEND_HASH_FOREACH_END(); + return result; +} + +#endif diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc new file mode 100644 index 00000000000..a87eae0db10 --- /dev/null +++ b/ext-src/swoole_thread_arraylist.cc @@ -0,0 +1,240 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_arraylist_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_arraylist_ce; +static zend_object_handlers swoole_thread_arraylist_handlers; + +struct ThreadArrayListObject { + ZendArray *list; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_arraylist, __construct); +static PHP_METHOD(swoole_thread_arraylist, offsetGet); +static PHP_METHOD(swoole_thread_arraylist, offsetExists); +static PHP_METHOD(swoole_thread_arraylist, offsetSet); +static PHP_METHOD(swoole_thread_arraylist, offsetUnset); +static PHP_METHOD(swoole_thread_arraylist, find); +static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, incr); +static PHP_METHOD(swoole_thread_arraylist, decr); +static PHP_METHOD(swoole_thread_arraylist, clean); +static PHP_METHOD(swoole_thread_arraylist, toArray); +static PHP_METHOD(swoole_thread_arraylist, sort); +SW_EXTERN_C_END + +static sw_inline ThreadArrayListObject *arraylist_fetch_object(zend_object *obj) { + return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); +} + +static void arraylist_free_object(zend_object *object) { + ThreadArrayListObject *ao = arraylist_fetch_object(object); + if (ao->list) { + ao->list->del_ref(); + ao->list = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *arraylist_create_object(zend_class_entry *ce) { + ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); + zend_object_std_init(&ao->std, ce); + object_properties_init(&ao->std, ce); + ao->std.handlers = &swoole_thread_arraylist_handlers; + return &ao->std; +} + +static ThreadArrayListObject *arraylist_fetch_object_check(zval *zobject) { + ThreadArrayListObject *ao = arraylist_fetch_object(Z_OBJ_P(zobject)); + if (!ao->list) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return ao; +} + +ThreadResource *php_swoole_thread_arraylist_cast(zval *zobject) { + return arraylist_fetch_object_check(zobject)->list; +} + +void php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource) { + auto obj = arraylist_create_object(swoole_thread_arraylist_ce); + auto ao = (ThreadArrayListObject *) arraylist_fetch_object(obj); + ao->list = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +// clang-format off +static const zend_function_entry swoole_thread_arraylist_methods[] = { + PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetGet, arginfo_class_Swoole_Thread_ArrayList_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, find, arginfo_class_Swoole_Thread_ArrayList_find, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, incr, arginfo_class_Swoole_Thread_ArrayList_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, decr, arginfo_class_Swoole_Thread_ArrayList_decr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, toArray, arginfo_class_Swoole_Thread_ArrayList_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, sort, arginfo_class_Swoole_Thread_ArrayList_sort, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_arraylist_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); + swoole_thread_arraylist_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_arraylist, arraylist_create_object, arraylist_free_object, ThreadArrayListObject, std); + + zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +} + +static PHP_METHOD(swoole_thread_arraylist, __construct) { + zend_array *array = nullptr; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(array) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (ao->list != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + + if (array) { + if (!zend_array_is_list(array)) { + zend_throw_error(NULL, "the parameter $array must be an array of type list"); + return; + } + ao->list = ZendArray::from(array); + } else { + ao->list = new ZendArray(); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetGet) { + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_offsetGet(index, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetExists) { + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->index_offsetExists(index, return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + if (!ao->list->index_offsetSet(index, zvalue)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, incr) { + INIT_ARRAY_INCR_PARAMS + auto ao = arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_incr(zkey, zvalue, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, decr) { + INIT_ARRAY_INCR_PARAMS + auto ao = arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_decr(zkey, zvalue, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->index_offsetUnset(index); +} + +static PHP_METHOD(swoole_thread_arraylist, find) { + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->find(zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, count) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->count(return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, clean) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->clean(); +} + +static PHP_METHOD(swoole_thread_arraylist, toArray) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->to_array(return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, sort) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->sort(true); +} +#endif diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc new file mode 100644 index 00000000000..5d5dce07d73 --- /dev/null +++ b/ext-src/swoole_thread_atomic.cc @@ -0,0 +1,363 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_atomic_arginfo.h" +END_EXTERN_C() + +zend_class_entry *swoole_thread_atomic_ce; +static zend_object_handlers swoole_thread_atomic_handlers; + +zend_class_entry *swoole_thread_atomic_long_ce; +static zend_object_handlers swoole_thread_atomic_long_handlers; + +struct AtomicResource : public ThreadResource { + sw_atomic_t value; + + AtomicResource(zend_long _value) : ThreadResource() { + value = _value; + } + + ~AtomicResource() override { } +}; + +struct AtomicObject { + AtomicResource *atomic; + zend_object std; +}; + +static sw_inline AtomicObject *atomic_fetch_object(zend_object *obj) { + return (AtomicObject *) ((char *) obj - swoole_thread_atomic_handlers.offset); +} + +static sw_atomic_t *atomic_get_ptr(zval *zobject) { + return &atomic_fetch_object(Z_OBJ_P(zobject))->atomic->value; +} + +static void atomic_free_object(zend_object *object) { + AtomicObject *o = atomic_fetch_object(object); + if (o->atomic) { + o->atomic->del_ref(); + o->atomic = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *atomic_create_object(zend_class_entry *ce) { + AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce); + zend_object_std_init(&atomic->std, ce); + object_properties_init(&atomic->std, ce); + atomic->std.handlers = &swoole_thread_atomic_handlers; + + return &atomic->std; +} + +struct AtomicLongResource : public ThreadResource { + sw_atomic_long_t value; + + AtomicLongResource(zend_long _value) : ThreadResource() { + value = _value; + } + + ~AtomicLongResource() override { } +}; + +struct AtomicLongObject { + AtomicLongResource *atomic; + zend_object std; +}; + +static sw_inline AtomicLongObject *atomic_long_fetch_object(zend_object *obj) { + return (AtomicLongObject *) ((char *) obj - swoole_thread_atomic_long_handlers.offset); +} + +static sw_atomic_long_t *atomic_long_get_ptr(zval *zobject) { + return &atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic->value; +} + +static void atomic_long_free_object(zend_object *object) { + AtomicLongObject *o = atomic_long_fetch_object(object); + if (o->atomic) { + o->atomic->del_ref(); + o->atomic = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *atomic_long_create_object(zend_class_entry *ce) { + AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce); + zend_object_std_init(&atomic_long->std, ce); + object_properties_init(&atomic_long->std, ce); + atomic_long->std.handlers = &swoole_thread_atomic_long_handlers; + return &atomic_long->std; +} + +ThreadResource *php_swoole_thread_atomic_cast(zval *zobject) { + return atomic_fetch_object(Z_OBJ_P(zobject))->atomic; +} + +ThreadResource *php_swoole_thread_atomic_long_cast(zval *zobject) { + return atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic; +} + +void php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource) { + auto obj = atomic_create_object(swoole_thread_atomic_ce); + auto ao = (AtomicObject *) atomic_fetch_object(obj); + ao->atomic = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +void php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource) { + auto obj = atomic_long_create_object(swoole_thread_atomic_long_ce); + auto ao = (AtomicLongObject *) atomic_long_fetch_object(obj); + ao->atomic = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_atomic, __construct); +static PHP_METHOD(swoole_thread_atomic, add); +static PHP_METHOD(swoole_thread_atomic, sub); +static PHP_METHOD(swoole_thread_atomic, get); +static PHP_METHOD(swoole_thread_atomic, set); +static PHP_METHOD(swoole_thread_atomic, cmpset); +static PHP_METHOD(swoole_thread_atomic, wait); +static PHP_METHOD(swoole_thread_atomic, wakeup); + +static PHP_METHOD(swoole_thread_atomic_long, __construct); +static PHP_METHOD(swoole_thread_atomic_long, add); +static PHP_METHOD(swoole_thread_atomic_long, sub); +static PHP_METHOD(swoole_thread_atomic_long, get); +static PHP_METHOD(swoole_thread_atomic_long, set); +static PHP_METHOD(swoole_thread_atomic_long, cmpset); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_atomic_methods[] = +{ + PHP_ME(swoole_thread_atomic, __construct, arginfo_class_Swoole_Thread_Atomic___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, add, arginfo_class_Swoole_Thread_Atomic_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, sub, arginfo_class_Swoole_Thread_Atomic_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, get, arginfo_class_Swoole_Thread_Atomic_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, set, arginfo_class_Swoole_Thread_Atomic_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wait, arginfo_class_Swoole_Thread_Atomic_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wakeup, arginfo_class_Swoole_Thread_Atomic_wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, cmpset, arginfo_class_Swoole_Thread_Atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static const zend_function_entry swoole_thread_atomic_long_methods[] = +{ + PHP_ME(swoole_thread_atomic_long, __construct, arginfo_class_Swoole_Thread_Atomic_Long___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, add, arginfo_class_Swoole_Thread_Atomic_Long_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, sub, arginfo_class_Swoole_Thread_Atomic_Long_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, get, arginfo_class_Swoole_Thread_Atomic_Long_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, set, arginfo_class_Swoole_Thread_Atomic_Long_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, cmpset, arginfo_class_Swoole_Thread_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_atomic_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_atomic, "Swoole\\Thread\\Atomic", nullptr, swoole_thread_atomic_methods); + swoole_thread_atomic_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic, + atomic_create_object, + atomic_free_object, + AtomicObject, + std); + + SW_INIT_CLASS_ENTRY( + swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); + swoole_thread_atomic_long_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic_long, + atomic_long_create_object, + atomic_long_free_object, + AtomicLongObject, + std); +} + +PHP_METHOD(swoole_thread_atomic, __construct) { + auto o = atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->atomic) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + o->atomic = new AtomicResource(value); +} + +PHP_METHOD(swoole_thread_atomic, add) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic, sub) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic, get) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic); +} + +PHP_METHOD(swoole_thread_atomic, set) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic = (uint32_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic, cmpset) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value)); +} + +PHP_METHOD(swoole_thread_atomic, wait) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + double timeout = 1.0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); +} + +PHP_METHOD(swoole_thread_atomic, wakeup) { + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); + zend_long n = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(n) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); +} + +PHP_METHOD(swoole_thread_atomic_long, __construct) { + auto o = atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->atomic) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + o->atomic = new AtomicLongResource(value); +} + +PHP_METHOD(swoole_thread_atomic_long, add) { + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, sub) { + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, get) { + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic_long); +} + +PHP_METHOD(swoole_thread_atomic_long, set) { + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic_long = (sw_atomic_long_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic_long, cmpset) { + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); +} + +#endif diff --git a/ext-src/swoole_thread_barrier.cc b/ext-src/swoole_thread_barrier.cc new file mode 100644 index 00000000000..9bf3539bf59 --- /dev/null +++ b/ext-src/swoole_thread_barrier.cc @@ -0,0 +1,149 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_barrier_arginfo.h" +END_EXTERN_C() + +using swoole::Barrier; + +zend_class_entry *swoole_thread_barrier_ce; +static zend_object_handlers swoole_thread_barrier_handlers; + +struct BarrierResource : public ThreadResource { + Barrier barrier_; + BarrierResource(int count) : ThreadResource() { + barrier_.init(false, count); + } + void wait() { + barrier_.wait(); + } + ~BarrierResource() override { + barrier_.destroy(); + } +}; + +struct BarrierObject { + BarrierResource *barrier; + zend_object std; +}; + +static sw_inline BarrierObject *barrier_fetch_object(zend_object *obj) { + return (BarrierObject *) ((char *) obj - swoole_thread_barrier_handlers.offset); +} + +static BarrierResource *barrier_get_ptr(zval *zobject) { + return barrier_fetch_object(Z_OBJ_P(zobject))->barrier; +} + +static BarrierResource *barrier_get_and_check_ptr(zval *zobject) { + BarrierResource *barrier = barrier_get_ptr(zobject); + if (UNEXPECTED(!barrier)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return barrier; +} + +static void barrier_free_object(zend_object *object) { + BarrierObject *bo = barrier_fetch_object(object); + if (bo->barrier) { + bo->barrier->del_ref(); + bo->barrier = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *barrier_create_object(zend_class_entry *ce) { + BarrierObject *bo = (BarrierObject *) zend_object_alloc(sizeof(BarrierObject), ce); + zend_object_std_init(&bo->std, ce); + object_properties_init(&bo->std, ce); + bo->std.handlers = &swoole_thread_barrier_handlers; + return &bo->std; +} + +ThreadResource *php_swoole_thread_barrier_cast(zval *zobject) { + return barrier_fetch_object(Z_OBJ_P(zobject))->barrier; +} + +void php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource) { + auto obj = barrier_create_object(swoole_thread_barrier_ce); + auto bo = (BarrierObject *) barrier_fetch_object(obj); + bo->barrier = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_barrier, __construct); +static PHP_METHOD(swoole_thread_barrier, wait); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_barrier_methods[] = +{ + PHP_ME(swoole_thread_barrier, __construct, arginfo_class_Swoole_Thread_Barrier___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_barrier, wait, arginfo_class_Swoole_Thread_Barrier_wait, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_barrier_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_barrier, "Swoole\\Thread\\Barrier", nullptr, swoole_thread_barrier_methods); + swoole_thread_barrier_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_barrier, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_barrier, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_barrier, + barrier_create_object, + barrier_free_object, + BarrierObject, + std); +} + +static PHP_METHOD(swoole_thread_barrier, __construct) { + auto bo = barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (bo->barrier != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + + zend_long count; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END(); + + if (count < 2) { + zend_throw_exception( + swoole_exception_ce, "The parameter $count must be greater than 1", SW_ERROR_INVALID_PARAMS); + return; + } + + bo->barrier = new BarrierResource(count); +} + +static PHP_METHOD(swoole_thread_barrier, wait) { + BarrierResource *barrier = barrier_get_and_check_ptr(ZEND_THIS); + if (barrier) { + barrier->wait(); + } +} + +#endif diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc new file mode 100644 index 00000000000..e6c5526378a --- /dev/null +++ b/ext-src/swoole_thread_lock.cc @@ -0,0 +1,222 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_lock_arginfo.h" +END_EXTERN_C() + +using swoole::Lock; +using swoole::Mutex; +#ifdef HAVE_SPINLOCK +using swoole::SpinLock; +#endif +#ifdef HAVE_RWLOCK +using swoole::RWLock; +#endif + +zend_class_entry *swoole_thread_lock_ce; +static zend_object_handlers swoole_thread_lock_handlers; + +struct LockResource : public ThreadResource { + Lock *lock_; + LockResource(int type) : ThreadResource() { + switch (type) { +#ifdef HAVE_SPINLOCK + case Lock::SPIN_LOCK: + lock_ = new SpinLock(0); + break; +#endif +#ifdef HAVE_RWLOCK + case Lock::RW_LOCK: + lock_ = new RWLock(0); + break; +#endif + case Lock::MUTEX: + default: + lock_ = new Mutex(0); + break; + } + } + ~LockResource() override { + delete lock_; + } +}; + +struct LockObject { + LockResource *lock; + zend_object std; +}; + +static sw_inline LockObject *lock_fetch_object(zend_object *obj) { + return (LockObject *) ((char *) obj - swoole_thread_lock_handlers.offset); +} + +static Lock *lock_get_ptr(zval *zobject) { + return lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_; +} + +static Lock *lock_get_and_check_ptr(zval *zobject) { + Lock *lock = lock_get_ptr(zobject); + if (!lock) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return lock; +} + +static void lock_free_object(zend_object *object) { + LockObject *o = lock_fetch_object(object); + if (o->lock) { + o->lock->del_ref(); + o->lock = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *lock_create_object(zend_class_entry *ce) { + LockObject *lock = (LockObject *) zend_object_alloc(sizeof(LockObject), ce); + zend_object_std_init(&lock->std, ce); + object_properties_init(&lock->std, ce); + lock->std.handlers = &swoole_thread_lock_handlers; + return &lock->std; +} + +ThreadResource *php_swoole_thread_lock_cast(zval *zobject) { + return lock_fetch_object(Z_OBJ_P(zobject))->lock; +} + +void php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource) { + auto obj = lock_create_object(swoole_thread_lock_ce); + auto lo = (LockObject *) lock_fetch_object(obj); + lo->lock = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_lock, __construct); +static PHP_METHOD(swoole_thread_lock, __destruct); +static PHP_METHOD(swoole_thread_lock, lock); +static PHP_METHOD(swoole_thread_lock, lockwait); +static PHP_METHOD(swoole_thread_lock, trylock); +static PHP_METHOD(swoole_thread_lock, lock_read); +static PHP_METHOD(swoole_thread_lock, trylock_read); +static PHP_METHOD(swoole_thread_lock, unlock); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_lock_methods[] = +{ + PHP_ME(swoole_thread_lock, __construct, arginfo_class_Swoole_Thread_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, __destruct, arginfo_class_Swoole_Thread_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock, arginfo_class_Swoole_Thread_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lockwait, arginfo_class_Swoole_Thread_Lock_locakwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock, arginfo_class_Swoole_Thread_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock_read, arginfo_class_Swoole_Thread_Lock_lock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock_read, arginfo_class_Swoole_Thread_Lock_trylock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, unlock, arginfo_class_Swoole_Thread_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_lock_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_lock, "Swoole\\Thread\\Lock", nullptr, swoole_thread_lock_methods); + swoole_thread_lock_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_lock, lock_create_object, lock_free_object, LockObject, std); + + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); +#ifdef HAVE_RWLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); +#endif +#ifdef HAVE_SPINLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); +#endif + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_thread_lock, __construct) { + auto o = lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->lock != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_long type = swoole::Lock::MUTEX; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END(); + + o->lock = new LockResource(type); +} + +static PHP_METHOD(swoole_thread_lock, __destruct) {} + +static PHP_METHOD(swoole_thread_lock, lock) { + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_thread_lock, lockwait) { + double timeout = 1.0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + if (lock->get_type() != Lock::MUTEX) { + zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); + RETURN_FALSE; + } + Mutex *mutex = dynamic_cast(lock); + if (mutex == nullptr) { + zend_throw_exception(swoole_exception_ce, "wrong lock type", -3); + RETURN_FALSE; + } + SW_LOCK_CHECK_RETURN(mutex->lock_wait((int) timeout * 1000)); +} + +static PHP_METHOD(swoole_thread_lock, unlock) { + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock) { + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock_read) { + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock_rd()); +} + +static PHP_METHOD(swoole_thread_lock, lock_read) { + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock_rd()); +} + +#endif diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc new file mode 100644 index 00000000000..da68a756862 --- /dev/null +++ b/ext-src/swoole_thread_map.cc @@ -0,0 +1,312 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_map_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_map_ce; +static zend_object_handlers swoole_thread_map_handlers; + +struct ThreadMapObject { + ZendArray *map; + zend_object std; +}; + +static sw_inline ThreadMapObject *map_fetch_object(zend_object *obj) { + return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); +} + +static void map_free_object(zend_object *object) { + ThreadMapObject *mo = map_fetch_object(object); + if (mo->map) { + mo->map->del_ref(); + mo->map = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *map_create_object(zend_class_entry *ce) { + ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); + zend_object_std_init(&mo->std, ce); + object_properties_init(&mo->std, ce); + mo->std.handlers = &swoole_thread_map_handlers; + return &mo->std; +} + +static ThreadMapObject *map_fetch_object_check(zval *zobject) { + ThreadMapObject *map = map_fetch_object(Z_OBJ_P(zobject)); + if (!map->map) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return map; +} + +ThreadResource *php_swoole_thread_map_cast(zval *zobject) { + return map_fetch_object(Z_OBJ_P(zobject))->map; +} + +void php_swoole_thread_map_create(zval *return_value, ThreadResource *resource) { + auto obj = map_create_object(swoole_thread_map_ce); + auto mo = (ThreadMapObject *) map_fetch_object(obj); + mo->map = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_map, __construct); +static PHP_METHOD(swoole_thread_map, offsetGet); +static PHP_METHOD(swoole_thread_map, offsetExists); +static PHP_METHOD(swoole_thread_map, offsetSet); +static PHP_METHOD(swoole_thread_map, offsetUnset); +static PHP_METHOD(swoole_thread_map, find); +static PHP_METHOD(swoole_thread_map, count); +static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, values); +static PHP_METHOD(swoole_thread_map, incr); +static PHP_METHOD(swoole_thread_map, decr); +static PHP_METHOD(swoole_thread_map, add); +static PHP_METHOD(swoole_thread_map, update); +static PHP_METHOD(swoole_thread_map, clean); +static PHP_METHOD(swoole_thread_map, toArray); +static PHP_METHOD(swoole_thread_map, sort); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_map_methods[] = { + PHP_ME(swoole_thread_map, __construct, arginfo_class_Swoole_Thread_Map___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetGet, arginfo_class_Swoole_Thread_Map_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, find, arginfo_class_Swoole_Thread_Map_find, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, incr, arginfo_class_Swoole_Thread_Map_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, decr, arginfo_class_Swoole_Thread_Map_decr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, add, arginfo_class_Swoole_Thread_Map_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, update, arginfo_class_Swoole_Thread_Map_update, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, values, arginfo_class_Swoole_Thread_Map_values, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, toArray, arginfo_class_Swoole_Thread_Map_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, sort, arginfo_class_Swoole_Thread_Map_sort, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_map_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); + swoole_thread_map_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_map, map_create_object, map_free_object, ThreadMapObject, std); + + zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); +} + +static PHP_METHOD(swoole_thread_map, __construct) { + zend_array *array = nullptr; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(array) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (mo->map != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + + if (array) { + mo->map = ZendArray::from(array); + } else { + mo->map = new ZendArray(); + } +} + +static int handle_array_key(zval *key, zend_ulong *idx) { + switch (Z_TYPE_P(key)) { + case IS_STRING: + return _zend_handle_numeric_str(Z_STRVAL_P(key), Z_STRLEN_P(key), idx) ? IS_LONG : IS_STRING; + case IS_LONG: + *idx = Z_LVAL_P(key); + return IS_LONG; + case IS_NULL: + return IS_NULL; + case IS_DOUBLE: + *idx = zend_dval_to_lval_safe(Z_DVAL_P(key)); + return IS_LONG; + case IS_FALSE: + *idx = 0; + return IS_LONG; + case IS_TRUE: + *idx = 1; + return IS_LONG; + case IS_RESOURCE: + zend_use_resource_as_offset(key); + *idx = Z_RES_HANDLE_P(key); + return IS_LONG; + default: + zend_argument_type_error(1, "Illegal offset type"); + return IS_UNDEF; + } +} + +#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ + zend_ulong idx; \ + int type_of_key = handle_array_key(zkey, &idx); \ + if (type_of_key == IS_LONG) { \ + array->intkey_##method(idx, ##__VA_ARGS__); \ + } else if (type_of_key == IS_STRING) { \ + array->strkey_##method(zkey, ##__VA_ARGS__); \ + } else if (type_of_key == IS_NULL) { \ + zval empty_str; \ + ZVAL_EMPTY_STRING(&empty_str); \ + array->strkey_##method(&empty_str, ##__VA_ARGS__); \ + } else { \ + zend_type_error("Illegal offset type"); \ + } + +static PHP_METHOD(swoole_thread_map, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); +} + +static PHP_METHOD(swoole_thread_map, incr) { + INIT_ARRAY_INCR_PARAMS + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, incr, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, decr) { + INIT_ARRAY_INCR_PARAMS + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, decr, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, add) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, add, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, update) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, update, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetUnset) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); +} + +static PHP_METHOD(swoole_thread_map, find) { + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->find(zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, count) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->count(return_value); +} + +static PHP_METHOD(swoole_thread_map, keys) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->keys(return_value); +} + +static PHP_METHOD(swoole_thread_map, values) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->values(return_value); +} + +static PHP_METHOD(swoole_thread_map, toArray) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->to_array(return_value); +} + +static PHP_METHOD(swoole_thread_map, clean) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->clean(); +} + +static PHP_METHOD(swoole_thread_map, sort) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->sort(false); +} +#endif diff --git a/ext-src/swoole_thread_queue.cc b/ext-src/swoole_thread_queue.cc new file mode 100644 index 00000000000..e9eed1dde8b --- /dev/null +++ b/ext-src/swoole_thread_queue.cc @@ -0,0 +1,258 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" +#include "stubs/php_swoole_thread_queue_arginfo.h" + +#include +#include + +zend_class_entry *swoole_thread_queue_ce; +static zend_object_handlers swoole_thread_queue_handlers; + +struct Queue : ThreadResource { + std::queue queue; + std::mutex lock_; + std::condition_variable cv_; + + enum { + NOTIFY_NONE = 0, + NOTIFY_ONE = 1, + NOTIFY_ALL = 2, + }; + + Queue() : ThreadResource(), queue() {} + + ~Queue() override { + clean(); + } + + void push(zval *zvalue) { + auto item = new ArrayItem(zvalue); + lock_.lock(); + queue.push(item); + lock_.unlock(); + } + + void pop(zval *return_value) { + ArrayItem *item = nullptr; + lock_.lock(); + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + } + lock_.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void push_notify(zval *zvalue, bool notify_all) { + auto item = new ArrayItem(zvalue); + std::unique_lock _lock(lock_); + queue.push(item); + if (notify_all) { + cv_.notify_all(); + } else { + cv_.notify_one(); + } + } + + void pop_wait(zval *return_value, double timeout) { + ArrayItem *item = nullptr; + std::unique_lock _lock(lock_); + SW_LOOP { + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + break; + } else { + if (timeout > 0) { + if (cv_.wait_for(_lock, std::chrono::duration(timeout)) == std::cv_status::timeout) { + break; + } + } else { + cv_.wait(_lock); + } + // All threads have been awakened, + // but the data has already been acquired by other thread, returning NULL. + if (queue.empty()) { + RETVAL_NULL(); + swoole_set_last_error(SW_ERROR_NO_PAYLOAD); + break; + } + } + } + _lock.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void count(zval *return_value) { + lock_.lock(); + RETVAL_LONG(queue.size()); + lock_.unlock(); + } + + void clean() { + lock_.lock(); + while (!queue.empty()) { + ArrayItem *item = queue.front(); + delete item; + queue.pop(); + } + lock_.unlock(); + } +}; + +struct ThreadQueueObject { + Queue *queue; + zend_object std; +}; + +static sw_inline ThreadQueueObject *queue_fetch_object(zend_object *obj) { + return (ThreadQueueObject *) ((char *) obj - swoole_thread_queue_handlers.offset); +} + +static void queue_free_object(zend_object *object) { + ThreadQueueObject *qo = queue_fetch_object(object); + if (qo->queue) { + qo->queue->del_ref(); + qo->queue = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *queue_create_object(zend_class_entry *ce) { + ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); + zend_object_std_init(&qo->std, ce); + object_properties_init(&qo->std, ce); + qo->std.handlers = &swoole_thread_queue_handlers; + return &qo->std; +} + +ThreadQueueObject *queue_fetch_object_check(zval *zobject) { + ThreadQueueObject *qo = queue_fetch_object(Z_OBJ_P(zobject)); + if (!qo->queue) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return qo; +} + +ThreadResource *php_swoole_thread_queue_cast(zval *zobject) { + return queue_fetch_object(Z_OBJ_P(zobject))->queue; +} + +void php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource) { + auto obj = queue_create_object(swoole_thread_queue_ce); + auto qo = (ThreadQueueObject *) queue_fetch_object(obj); + qo->queue = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_queue, __construct); +static PHP_METHOD(swoole_thread_queue, push); +static PHP_METHOD(swoole_thread_queue, pop); +static PHP_METHOD(swoole_thread_queue, count); +static PHP_METHOD(swoole_thread_queue, clean); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_queue_methods[] = { + PHP_ME(swoole_thread_queue, __construct, arginfo_class_Swoole_Thread_Queue___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, push, arginfo_class_Swoole_Thread_Queue_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_queue_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); + swoole_thread_queue_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; + SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_queue, queue_create_object, queue_free_object, ThreadQueueObject, std); + + zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); + + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); +} + +static PHP_METHOD(swoole_thread_queue, __construct) { + auto qo = queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (qo->queue != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + qo->queue = new Queue(); +} + +static PHP_METHOD(swoole_thread_queue, push) { + zval *zvalue; + zend_long notify_which = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zvalue) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(notify_which) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = queue_fetch_object_check(ZEND_THIS); + if (notify_which > 0) { + qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); + } else { + qo->queue->push(zvalue); + } +} + +static PHP_METHOD(swoole_thread_queue, pop) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = queue_fetch_object_check(ZEND_THIS); + if (timeout == 0) { + qo->queue->pop(return_value); + } else { + qo->queue->pop_wait(return_value, timeout); + } +} + +static PHP_METHOD(swoole_thread_queue, count) { + auto qo = queue_fetch_object_check(ZEND_THIS); + qo->queue->count(return_value); +} + +static PHP_METHOD(swoole_thread_queue, clean) { + auto qo = queue_fetch_object_check(ZEND_THIS); + qo->queue->clean(); +} + +#endif diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 10394ee9fa1..1011cb624bd 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -12,16 +12,21 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_cxx.h" +#include "php_swoole_process.h" #include "swoole_server.h" #include "ext/spl/spl_array.h" +BEGIN_EXTERN_C() +#include "stubs/php_swoole_timer_arginfo.h" +END_EXTERN_C() + using swoole::Timer; using swoole::TimerNode; using zend::Function; @@ -32,7 +37,6 @@ static zend_object_handlers swoole_timer_handlers; static zend_class_entry *swoole_timer_iterator_ce; SW_EXTERN_C_BEGIN -static PHP_FUNCTION(swoole_timer_set); static PHP_FUNCTION(swoole_timer_after); static PHP_FUNCTION(swoole_timer_tick); static PHP_FUNCTION(swoole_timer_exists); @@ -44,73 +48,55 @@ static PHP_FUNCTION(swoole_timer_clear_all); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_after, 0, 0, 2) - ZEND_ARG_INFO(0, ms) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_tick, 0, 0, 2) - ZEND_ARG_INFO(0, ms) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_exists, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_info, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_clear, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_timer_methods[] = { - ZEND_FENTRY(set, ZEND_FN(swoole_timer_set), arginfo_swoole_timer_set, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(info, ZEND_FN(swoole_timer_info), arginfo_swoole_timer_info, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(stats, ZEND_FN(swoole_timer_stats), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(list, ZEND_FN(swoole_timer_list), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(clear, ZEND_FN(swoole_timer_clear), arginfo_swoole_timer_clear, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(clearAll, ZEND_FN(swoole_timer_clear_all), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(info, ZEND_FN(swoole_timer_info), arginfo_swoole_timer_info, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(stats, ZEND_FN(swoole_timer_stats), arginfo_swoole_timer_stats, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(list, ZEND_FN(swoole_timer_list), arginfo_swoole_timer_list, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(clear, ZEND_FN(swoole_timer_clear), arginfo_swoole_timer_clear, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(clearAll, ZEND_FN(swoole_timer_clear_all), arginfo_swoole_timer_clear_all, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on void php_swoole_timer_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_timer, "Swoole\\Timer", "swoole_timer", nullptr, swoole_timer_methods); + SW_INIT_CLASS_ENTRY(swoole_timer, "Swoole\\Timer", nullptr, swoole_timer_methods); SW_SET_CLASS_CREATE(swoole_timer, sw_zend_create_object_deny); - SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, - "Swoole\\Timer\\Iterator", - "swoole_timer_iterator", - nullptr, - nullptr, - spl_ce_ArrayIterator); - - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "tick", CG(function_table), "swoole_timer_tick"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "exists", CG(function_table), "swoole_timer_exists"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "info", CG(function_table), "swoole_timer_info"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "stats", CG(function_table), "swoole_timer_stats"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "list", CG(function_table), "swoole_timer_list"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clear", CG(function_table), "swoole_timer_clear"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clearAll", CG(function_table), "swoole_timer_clear_all"); + SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, "Swoole\\Timer\\Iterator", nullptr, nullptr, spl_ce_ArrayIterator); + + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after", arginfo_swoole_timer_tick); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "tick", CG(function_table), "swoole_timer_tick", arginfo_swoole_timer_after); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "info", CG(function_table), "swoole_timer_info", arginfo_swoole_timer_info); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "list", CG(function_table), "swoole_timer_list", arginfo_swoole_timer_list); + + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "exists", + CG(function_table), + "swoole_timer_exists", + arginfo_swoole_timer_exists); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "stats", + CG(function_table), + "swoole_timer_stats", + arginfo_swoole_timer_stats); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "clear", + CG(function_table), + "swoole_timer_clear", + arginfo_swoole_timer_clear); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "clearAll", + CG(function_table), + "swoole_timer_clear_all", + arginfo_swoole_timer_clear_all); SW_REGISTER_LONG_CONSTANT("SWOOLE_TIMER_MIN_MS", SW_TIMER_MIN_MS); SW_REGISTER_DOUBLE_CONSTANT("SWOOLE_TIMER_MIN_SEC", SW_TIMER_MIN_SEC); @@ -163,6 +149,18 @@ static void timer_callback(Timer *timer, TimerNode *tnode) { } } +static bool timer_if_use_reactor() { + auto server = sw_server(); + if (server) { + return server->is_user_worker() || (server->is_task_worker() && server->task_enable_coroutine); + } + auto process_pool = sw_process_pool(); + if (process_pool) { + return !process_pool->is_master(); + } + return true; +} + static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { zend_long ms; Function *fci = (Function *) ecalloc(1, sizeof(Function)); @@ -181,12 +179,11 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { RETURN_FALSE; } - // no server || user worker || task process with async mode - if (!sw_server() || sw_server()->is_user_worker() || (sw_server()->is_task_worker() && sw_server()->task_enable_coroutine)) { + if (UNEXPECTED(!sw_reactor() && timer_if_use_reactor())) { php_swoole_check_reactor(); } - tnode = swoole_timer_add(ms, persistent, timer_callback, fci); + tnode = swoole_timer_add((long) ms, persistent, timer_callback, fci); if (UNEXPECTED(!tnode)) { php_swoole_fatal_error(E_WARNING, "add timer failed"); goto _failed; @@ -213,21 +210,6 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { RETURN_LONG(tnode->id); } -static PHP_FUNCTION(swoole_timer_set) { - zval *zset = nullptr; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - HashTable *vht = Z_ARRVAL_P(zset); - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - SWOOLE_G(enable_coroutine) = zval_is_true(ztmp); - } -} - static PHP_FUNCTION(swoole_timer_after) { timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index b94cfcaff8d..1ab926a18a3 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,6 +18,7 @@ SW_EXTERN_C_BEGIN #include "ext/standard/sha1.h" +#include "stubs/php_swoole_websocket_arginfo.h" SW_EXTERN_C_END #include "swoole_base64.h" @@ -43,6 +44,8 @@ static zend_object_handlers swoole_websocket_frame_handlers; static zend_class_entry *swoole_websocket_closeframe_ce; static zend_object_handlers swoole_websocket_closeframe_handlers; +static SW_THREAD_LOCAL String *swoole_websocket_buffer = nullptr; + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_websocket_server, push); static PHP_METHOD(swoole_websocket_server, isEstablished); @@ -54,52 +57,21 @@ static PHP_METHOD(swoole_websocket_frame, __toString); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_push, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_disconnect, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, code) - ZEND_ARG_INFO(0, reason) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_pack, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_unpack, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_isEstablished, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_frame_void, 0, 0, 0) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_websocket_server_methods[] = { - PHP_ME(swoole_websocket_server, push, arginfo_swoole_websocket_server_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, disconnect, arginfo_swoole_websocket_server_disconnect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, isEstablished, arginfo_swoole_websocket_server_isEstablished, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, pack, arginfo_swoole_websocket_server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_websocket_server, unpack, arginfo_swoole_websocket_server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, push, arginfo_class_Swoole_WebSocket_Server_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, disconnect, arginfo_class_Swoole_WebSocket_Server_disconnect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, isEstablished, arginfo_class_Swoole_WebSocket_Server_isEstablished, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, pack, arginfo_class_Swoole_WebSocket_Server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, unpack, arginfo_class_Swoole_WebSocket_Server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; const zend_function_entry swoole_websocket_frame_methods[] = { - PHP_ME(swoole_websocket_frame, __toString, arginfo_swoole_websocket_frame_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, pack, arginfo_swoole_websocket_server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_websocket_server, unpack, arginfo_swoole_websocket_server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_frame, __toString, arginfo_class_Swoole_WebSocket_Frame___toString, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, pack, arginfo_class_Swoole_WebSocket_Frame_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, unpack, arginfo_class_Swoole_WebSocket_Frame_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -151,18 +123,24 @@ void php_swoole_websocket_frame_unpack_ex(String *data, zval *zframe, uchar unco return; } - WebSocket::decode(&frame, data); + if (!WebSocket::decode(&frame, data)) { + swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR); + ZVAL_FALSE(zframe); + return; + } + flags = WebSocket::get_flags(&frame); #ifdef SW_HAVE_ZLIB if (uncompress && frame.header.RSV1) { - swoole_zlib_buffer->clear(); - if (!websocket_message_uncompress(swoole_zlib_buffer, frame.payload, frame.payload_length)) { + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (!websocket_message_uncompress(zlib_buffer, frame.payload, frame.payload_length)) { swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR); ZVAL_FALSE(zframe); return; } - frame.payload = swoole_zlib_buffer->str; - frame.payload_length = swoole_zlib_buffer->length; + frame.payload = zlib_buffer->str; + frame.payload_length = zlib_buffer->length; flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); } #endif @@ -207,23 +185,24 @@ static sw_inline int php_swoole_websocket_frame_pack_ex(String *buffer, if (!allow_compress) { flags ^= WebSocket::FLAG_COMPRESS; } else if (length > 0) { - swoole_zlib_buffer->clear(); - if (websocket_message_compress(swoole_zlib_buffer, data, length, Z_DEFAULT_COMPRESSION)) { - data = swoole_zlib_buffer->str; - length = swoole_zlib_buffer->length; + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (websocket_message_compress(zlib_buffer, data, length, Z_DEFAULT_COMPRESSION)) { + data = zlib_buffer->str; + length = zlib_buffer->length; flags |= WebSocket::FLAG_RSV1; } } } #endif + // TODO: send data in zlib_buffer directly, do not copy to http_buffer switch (opcode) { case WebSocket::OPCODE_CLOSE: return WebSocket::pack_close_frame(buffer, code, data, length, flags); default: - WebSocket::encode(buffer, data, length, opcode, flags); + return WebSocket::encode(buffer, data, length, opcode, flags) ? SW_OK : SW_ERR; } - return SW_OK; } int php_swoole_websocket_frame_pack_ex( @@ -240,7 +219,7 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ zval *ztmp = nullptr; zdata = nullptr; - if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 0))) { + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 1))) { opcode = zval_get_long(ztmp); } if (opcode == WebSocket::OPCODE_CLOSE) { @@ -254,14 +233,14 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ } } if (!zdata && - (ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 0))) { + (ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 1))) { zdata = ztmp; } - if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FLAGS), 0))) { + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FLAGS), 1))) { flags = zval_get_long(ztmp) & WebSocket::FLAGS_ALL; } if ((ztmp = sw_zend_read_property_not_null_ex( - swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FINISH), 0))) { + swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FINISH), 1))) { if (zval_is_true(ztmp)) { flags |= WebSocket::FLAG_FIN; } else { @@ -273,18 +252,33 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ buffer, zdata, opcode, code, flags & WebSocket::FLAGS_ALL, mask, allow_compress); } +void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { + auto cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); + if (cb) { + zval args[3]; + args[0] = *php_swoole_server_zval_ptr(serv); + args[1] = *ctx->request.zobject; + args[2] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(cb, 3, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error( + E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); + serv->close(ctx->fd, false); + } + } +} + void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); return; } - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); - if (fci_cache) { + auto cb = php_swoole_server_get_callback(serv, conn->server_fd, SW_SERVER_CB_onOpen); + if (cb) { zval args[2]; - args[0] = *((zval *) serv->private_data_2); + args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(ctx->fd, false); } @@ -349,10 +343,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { ctx->set_header(ZEND_STRL("Sec-WebSocket-Accept"), sec_buf, sec_len, false); ctx->set_header(ZEND_STRL("Sec-WebSocket-Version"), ZEND_STRL(SW_WEBSOCKET_VERSION), false); -#ifdef SW_HAVE_ZLIB - bool enable_websocket_compression = true; - bool websocket_compression = false; -#endif Server *serv = nullptr; Connection *conn = nullptr; @@ -360,29 +350,10 @@ bool swoole_websocket_handshake(HttpContext *ctx) { serv = (Server *) ctx->private_data; conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); return false; } -#ifdef SW_HAVE_ZLIB - enable_websocket_compression = serv->websocket_compression; -#endif } -#ifdef SW_HAVE_ZLIB - else { - enable_websocket_compression = ctx->websocket_compression; - } -#endif - -#ifdef SW_HAVE_ZLIB - if (enable_websocket_compression && (pData = zend_hash_str_find(ht, ZEND_STRL("sec-websocket-extensions"))) && - Z_TYPE_P(pData) == IS_STRING) { - std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); - if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { - websocket_compression = true; - ctx->set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); - } - } -#endif if (conn) { conn->websocket_status = WebSocket::STATUS_ACTIVE; @@ -393,9 +364,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { port->websocket_subprotocol.length(), false); } -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = conn->websocket_compression = websocket_compression; -#endif + swoole_websocket_onBeforeHandshakeResponse(serv, conn->server_fd, ctx); } else { Socket *sock = (Socket *) ctx->private_data; sock->open_length_check = 1; @@ -403,9 +372,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = WebSocket::get_package_length; -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = websocket_compression; -#endif } ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; @@ -578,26 +544,26 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { * RFC 7692 */ if (serv->websocket_compression && (flags & WebSocket::FLAG_RSV1)) { - swoole_zlib_buffer->clear(); - if (!websocket_message_uncompress(swoole_zlib_buffer, Z_STRVAL(zdata), Z_STRLEN(zdata))) { + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (!websocket_message_uncompress(zlib_buffer, Z_STRVAL(zdata), Z_STRLEN(zdata))) { zval_ptr_dtor(&zdata); return SW_OK; } zval_ptr_dtor(&zdata); - ZVAL_STRINGL(&zdata, swoole_zlib_buffer->str, swoole_zlib_buffer->length); + ZVAL_STRINGL(&zdata, zlib_buffer->str, zlib_buffer->length); flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); } #endif - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onMessage); + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onMessage); zval args[2]; - args[0] = *(zval *) serv->private_data_2; + args[0] = *php_swoole_server_zval_ptr(serv); php_swoole_websocket_construct_frame(&args[1], opcode, &zdata, flags); zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(&args[1]), ZEND_STRL("fd"), fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onMessage handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(fd, false); } @@ -622,31 +588,25 @@ int swoole_websocket_onHandshake(Server *serv, ListenPort *port, HttpContext *ct void php_swoole_websocket_server_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_websocket_server, "Swoole\\WebSocket\\Server", - "swoole_websocket_server", nullptr, swoole_websocket_server_methods, swoole_http_server); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_websocket_server); +#endif SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); - SW_INIT_CLASS_ENTRY(swoole_websocket_frame, - "Swoole\\WebSocket\\Frame", - "swoole_websocket_frame", - nullptr, - swoole_websocket_frame_methods); + SW_INIT_CLASS_ENTRY(swoole_websocket_frame, "Swoole\\WebSocket\\Frame", nullptr, swoole_websocket_frame_methods); + zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("flags"), WebSocket::FLAG_FIN, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_websocket_frame_ce, ZEND_STRL("finish"), ZEND_ACC_PUBLIC); - SW_INIT_CLASS_ENTRY_EX(swoole_websocket_closeframe, - "Swoole\\WebSocket\\CloseFrame", - "swoole_websocket_closeframe", - nullptr, - nullptr, - swoole_websocket_frame); + SW_INIT_CLASS_ENTRY_EX( + swoole_websocket_closeframe, "Swoole\\WebSocket\\CloseFrame", nullptr, nullptr, swoole_websocket_frame); zend_declare_property_long( swoole_websocket_closeframe_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_CLOSE, ZEND_ACC_PUBLIC); zend_declare_property_long( @@ -685,6 +645,9 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG", WebSocket::CLOSE_MESSAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_EXTENSION_MISSING", WebSocket::CLOSE_EXTENSION_MISSING); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_SERVER_ERROR", WebSocket::CLOSE_SERVER_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART", WebSocket::CLOSE_SERVICE_RESTART); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); /* swoole namespace }}} */ @@ -714,9 +677,29 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_MESSAGE_TOO_BIG", WebSocket::CLOSE_MESSAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_EXTENSION_MISSING", WebSocket::CLOSE_EXTENSION_MISSING); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_SERVER_ERROR", WebSocket::CLOSE_SERVER_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART", WebSocket::CLOSE_SERVICE_RESTART); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); } +void php_swoole_websocket_server_rinit() { + if (swoole_websocket_buffer == nullptr) { + swoole_websocket_buffer = swoole::make_string(SW_BUFFER_SIZE_BIG); + } +} + +void php_swoole_websocket_server_rshutdown() { + if (swoole_websocket_buffer) { + delete swoole_websocket_buffer; + swoole_websocket_buffer = nullptr; + } +} + +void php_swoole_websocket_server_mshutdown() { + +} + static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, String *buffer) { if (sw_unlikely(fd <= 0)) { php_swoole_fatal_error(E_WARNING, "fd[%ld] is invalid", fd); @@ -740,6 +723,7 @@ static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, S ZVAL_FALSE(return_value); php_swoole_server_send_yield(serv, fd, &_yield_data, return_value); ret = Z_BVAL_P(return_value); + zval_ptr_dtor(&_yield_data); } return ret; } @@ -772,14 +756,18 @@ static PHP_METHOD(swoole_websocket_server, disconnect) { char *data = nullptr; size_t length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ls", &fd, &code, &data, &length) == FAILURE) { - RETURN_FALSE; - } - swoole_http_buffer->clear(); - if (WebSocket::pack_close_frame(swoole_http_buffer, code, data, length, 0) < 0) { + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_LONG(fd) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(code) + Z_PARAM_STRING(data, length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_websocket_buffer->clear(); + if (WebSocket::pack_close_frame(swoole_websocket_buffer, code, data, length, 0) < 0) { RETURN_FALSE; } - RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_http_buffer, 1)); + RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_websocket_buffer, 1)); } static PHP_METHOD(swoole_websocket_server, push) { @@ -814,21 +802,21 @@ static PHP_METHOD(swoole_websocket_server, push) { Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); - php_swoole_fatal_error(E_WARNING, "session#%ld does not exists", fd); + php_swoole_fatal_error(E_WARNING, "session#" ZEND_LONG_FMT " does not exists", fd); RETURN_FALSE; } allow_compress = conn->websocket_compression; #endif - swoole_http_buffer->clear(); + swoole_websocket_buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(swoole_http_buffer, zdata, 0, allow_compress) < 0) { + if (php_swoole_websocket_frame_object_pack(swoole_websocket_buffer, zdata, 0, allow_compress) < 0) { swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED); RETURN_FALSE; } } else { if (php_swoole_websocket_frame_pack( - swoole_http_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, allow_compress) < 0) { + swoole_websocket_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, allow_compress) < 0) { swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED); RETURN_FALSE; } @@ -836,15 +824,33 @@ static PHP_METHOD(swoole_websocket_server, push) { switch (opcode) { case WebSocket::OPCODE_CLOSE: - RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_http_buffer, flags & WebSocket::FLAG_FIN)); + RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_websocket_buffer, flags & WebSocket::FLAG_FIN)); break; default: - RETURN_BOOL(swoole_websocket_server_push(serv, fd, swoole_http_buffer)); + RETURN_BOOL(swoole_websocket_server_push(serv, fd, swoole_websocket_buffer)); } } +static size_t websocket_get_frame_data_size(zval *zframe) { + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *ztmp = nullptr; + zval *zdata = nullptr; + + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 1))) { + opcode = zval_get_long(ztmp); + } + if (opcode == WebSocket::OPCODE_CLOSE) { + zdata = + sw_zend_read_property_not_null_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_REASON), 1); + } else { + zdata = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 1); + } + return (zdata && ZVAL_IS_STRING(zdata)) ? Z_STRLEN_P(zdata) : 0; +} + +#define FRAME_HEADER_LEN 16 + static PHP_METHOD(swoole_websocket_server, pack) { - String *buffer = sw_tg_buffer(); zval *zdata; zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; @@ -861,17 +867,26 @@ static PHP_METHOD(swoole_websocket_server, pack) { flags = zval_get_long(zflags); } - buffer->clear(); + String *buffer; + int result; if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, 0, 1) < 0) { - RETURN_EMPTY_STRING(); - } + buffer = + swoole::make_string(websocket_get_frame_data_size(zdata) + FRAME_HEADER_LEN, sw_zend_string_allocator()); + result = php_swoole_websocket_frame_object_pack(buffer, zdata, 0, 1); } else { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, 1) < 0) { - RETURN_EMPTY_STRING(); - } + buffer = swoole::make_string(Z_STRLEN_P(zdata) + FRAME_HEADER_LEN, sw_zend_string_allocator()); + result = php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, 1); } - RETURN_STRINGL(buffer->str, buffer->length); + + if (result < 0) { + delete buffer; + RETURN_EMPTY_STRING(); + } + + auto packed_str = zend::fetch_zend_string_by_val(buffer->str); + ZSTR_VAL(packed_str)[buffer->length] = '\0'; + ZSTR_LEN(packed_str) = buffer->length; + RETURN_STR(packed_str); } static PHP_METHOD(swoole_websocket_frame, __toString) { @@ -887,9 +902,9 @@ static PHP_METHOD(swoole_websocket_frame, __toString) { static PHP_METHOD(swoole_websocket_server, unpack) { String buffer = {}; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buffer.str, &buffer.length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(buffer.str, buffer.length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_swoole_websocket_frame_unpack(&buffer, return_value); } @@ -903,9 +918,9 @@ static PHP_METHOD(swoole_websocket_server, isEstablished) { zend_long session_id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &session_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(session_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(session_id); // not isEstablished diff --git a/include/helper/kqueue.h b/include/helper/kqueue.h index d24f3e1d24a..0d658e0d086 100644 --- a/include/helper/kqueue.h +++ b/include/helper/kqueue.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole.h b/include/swoole.h index e1441164569..4f19d095cff 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -56,8 +56,11 @@ #include #include +#include #include +#include #include +#include typedef unsigned long ulong_t; @@ -116,6 +119,9 @@ typedef unsigned long ulong_t; #define SW_ECHO_LEN_CYAN "\e[36m%.*s\e[0m" #define SW_ECHO_LEN_WHITE "\e[37m%.*s\e[0m" +#define SW_ECHO_RED_BG "\e[41m%s\e[0m" +#define SW_ECHO_GREEN_BG "\e[42m%s\e[0m" + #define SW_COLOR_RED 1 #define SW_COLOR_GREEN 2 #define SW_COLOR_YELLOW 3 @@ -166,7 +172,21 @@ typedef unsigned long ulong_t; #define SW_ASSERT(e) #define SW_ASSERT_1BYTE(v) #endif -#define SW_START_SLEEP usleep(100000) // sleep 1s,wait fork and pthread_create +#define SW_START_SLEEP usleep(100000) // sleep 0.1s, wait fork and pthread_create + +#ifdef SW_THREAD +#define SW_THREAD_LOCAL thread_local +extern std::mutex sw_thread_lock; +#else +#define SW_THREAD_LOCAL +#endif + +/** + * API naming rules + * ----------------------------------- + * - starts with swoole_, means it is ready or has been used as an external API + * - starts with sw_, internal use only + */ /*-----------------------------------Memory------------------------------------*/ void *sw_malloc(size_t size); @@ -190,11 +210,15 @@ class Timer; struct TimerNode; struct Event; class Pipe; +class MessageBus; namespace network { struct Socket; struct Address; } // namespace network class AsyncThreads; +#ifdef SW_USE_IOURING +class Iouring; +#endif namespace async { class ThreadPool; } @@ -204,6 +228,7 @@ struct DataHead; typedef int (*ReactorHandler)(Reactor *reactor, Event *event); typedef std::function Callback; typedef std::function TimerCallback; +typedef std::function TimerScheduler; } // namespace swoole typedef swoole::Reactor swReactor; @@ -223,9 +248,8 @@ typedef swoole::Event swEvent; #define SW_STREQ(str, len, const_str) swoole_streq(str, len, SW_STRL(const_str)) #define SW_STRCASEEQ(str, len, const_str) swoole_strcaseeq(str, len, SW_STRL(const_str)) -/* string contain */ -#define SW_STRCT(str, len, const_sub_str) swoole_strct(str, len, SW_STRL(const_sub_str)) -#define SW_STRCASECT(str, len, const_sub_str) swoole_strcasect(str, len, SW_STRL(const_sub_str)) +#define SW_STR_STARTS_WITH(str, len, const_sub_str) swoole_str_starts_with(str, len, SW_STRL(const_sub_str)) +#define SW_STR_ISTARTS_WITH(str, len, const_sub_str) swoole_str_istarts_with(str, len, SW_STRL(const_sub_str)) #if defined(SW_USE_JEMALLOC) || defined(SW_USE_TCMALLOC) #define sw_strdup swoole_strdup @@ -281,11 +305,11 @@ static inline unsigned int swoole_strcaseeq(const char *str1, size_t len1, const return (len1 == len2) && (strncasecmp(str1, str2, len1) == 0); } -static inline unsigned int swoole_strct(const char *pstr, size_t plen, const char *sstr, size_t slen) { +static inline unsigned int swoole_str_starts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) { return (plen >= slen) && (strncmp(pstr, sstr, slen) == 0); } -static inline unsigned int swoole_strcasect(const char *pstr, size_t plen, const char *sstr, size_t slen) { +static inline unsigned int swoole_str_istarts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) { return (plen >= slen) && (strncasecmp(pstr, sstr, slen) == 0); } @@ -308,6 +332,25 @@ static inline const char *swoole_strnstr(const char *haystack, return NULL; } +static inline const char *swoole_strncasestr(const char *haystack, + uint32_t haystack_length, + const char *needle, + uint32_t needle_length) { + assert(needle_length > 0); + uint32_t i; + + if (sw_likely(needle_length <= haystack_length)) { + for (i = 0; i < haystack_length - needle_length + 1; i++) { + if ((haystack[0] == needle[0]) && (0 == strncasecmp(haystack, needle, needle_length))) { + return haystack; + } + haystack++; + } + } + + return NULL; +} + static inline ssize_t swoole_strnpos(const char *haystack, uint32_t haystack_length, const char *needle, @@ -354,12 +397,14 @@ enum swResultCode { }; enum swReturnCode { + SW_SUCCESS = 0, SW_CONTINUE = 1, SW_WAIT = 2, SW_CLOSE = 3, SW_ERROR = 4, SW_READY = 5, SW_INVALID = 6, + SW_REDUCE_SIZE = 7, }; enum swFdType { @@ -387,6 +432,10 @@ enum swFdType { SW_FD_SIGNAL, SW_FD_DNS_RESOLVER, SW_FD_CARES, + /** + * io_uring + */ + SW_FD_IOURING, /** * SW_FD_USER or SW_FD_USER+n: for custom event */ @@ -428,6 +477,12 @@ enum swForkType { SW_FORK_PRECHECK = 1 << 3, }; +enum swTraverseOperation { + SW_TRAVERSE_KEEP = 0, + SW_TRAVERSE_REMOVE = 1, + SW_TRAVERSE_STOP = 2, +}; + //------------------------------------------------------------------------------- #define sw_yield() sched_yield() @@ -440,6 +495,7 @@ typedef unsigned char uchar; #define swoole_toupper(c) (uchar)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) void swoole_random_string(char *buf, size_t size); +void swoole_random_string(std::string &str, size_t size); size_t swoole_random_bytes(char *buf, size_t size); static sw_inline char *swoole_strlchr(char *p, char *last, char c) { @@ -482,17 +538,13 @@ enum swDNSLookupFlag { SW_DNS_LOOKUP_RANDOM = (1u << 11), }; -#ifdef __MACH__ -char *sw_error_(); -#define sw_error sw_error_() -#else -extern __thread char sw_error[SW_ERROR_MSG_SIZE]; -#endif +extern thread_local char sw_error[SW_ERROR_MSG_SIZE]; enum swProcessType { SW_PROCESS_MASTER = 1, SW_PROCESS_WORKER = 2, SW_PROCESS_MANAGER = 3, + SW_PROCESS_EVENTWORKER = 2, SW_PROCESS_TASKWORKER = 4, SW_PROCESS_USERWORKER = 5, }; @@ -523,7 +575,11 @@ int swoole_get_systemd_listen_fds(); void swoole_init(void); void swoole_clean(void); +void swoole_exit(int __status); pid_t swoole_fork(int flags); +pid_t swoole_fork_exec(const std::function &child_fn); +void swoole_thread_init(void); +void swoole_thread_clean(void); void swoole_redirect_stdout(int new_fd); int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream); int swoole_daemon(int nochdir, int noclose); @@ -538,17 +594,22 @@ int swoole_tmpfile(char *filename); typedef cpuset_t cpu_set_t; #endif int swoole_set_cpu_affinity(cpu_set_t *set); +int swoole_get_cpu_affinity(cpu_set_t *set); #endif -#ifdef HAVE_CLOCK_GETTIME -#define swoole_clock_gettime clock_gettime -#else -int swoole_clock_gettime(int which_clock, struct timespec *t); +#if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && \ + defined(CLOCK_MONOTONIC) +#ifndef HAVE_CLOCK_GETTIME +#define HAVE_CLOCK_GETTIME +#endif +#define swoole_clock_realtime(t) clock_gettime(CLOCK_REALTIME, t) +#elif defined(__APPLE__) +int swoole_clock_realtime(struct timespec *t); #endif static inline struct timespec swoole_time_until(int milliseconds) { struct timespec t; - swoole_clock_gettime(CLOCK_REALTIME, &t); + swoole_clock_realtime(&t); int sec = milliseconds / 1000; int msec = milliseconds - (sec * 1000); @@ -602,16 +663,42 @@ struct DataHead { struct EventData { DataHead info; char data[SW_IPC_BUFFER_SIZE]; + + uint32_t size() { + return sizeof(info) + len(); + } + + uint32_t len() { + return info.len; + } +}; + +struct SendData { + DataHead info; + const char *data; +}; + +struct RecvData { + DataHead info; + const char *data; }; struct ThreadGlobal { uint16_t id; uint8_t type; - uint8_t update_time; +#ifdef SW_THREAD + uint8_t process_type; + uint32_t process_id; +#endif String *buffer_stack; Reactor *reactor; Timer *timer; + TimerScheduler timer_scheduler; + MessageBus *message_bus; AsyncThreads *async_threads; +#ifdef SW_USE_IOURING + Iouring *iouring; +#endif uint32_t signal_listener_num; uint32_t co_signal_listener_num; int error; @@ -624,17 +711,44 @@ struct Allocator { void (*free)(void *ptr); }; +struct NameResolver { + enum Type { + TYPE_KERNEL, + TYPE_PHP, + TYPE_USER, + }; + struct Context { + int type; + double timeout; + void *private_data; + bool with_port; + bool cluster_; + bool final_; + std::function dtor; + + ~Context() { + if (private_data && dtor) { + dtor(this); + } + } + }; + std::function resolve; + void *private_data; + enum Type type; +}; + struct Global { uchar init : 1; uchar running : 1; - uchar use_signalfd : 1; uchar wait_signal : 1; uchar enable_signalfd : 1; uchar socket_dontwait : 1; uchar dns_lookup_random : 1; uchar use_async_resolver : 1; + uchar use_name_resolver : 1; + uchar enable_coroutine : 1; - int process_type; + uint8_t process_type; uint32_t process_id; TaskId current_task_id; pid_t pid; @@ -651,7 +765,6 @@ struct Global { uint32_t pagesize; struct utsname uname; uint32_t max_sockets; - uint32_t max_concurrency; //-----------------------[Memory]-------------------------- MemoryPool *memory_pool; Allocator std_allocator; @@ -663,9 +776,15 @@ struct Global { int dns_tries; std::string dns_resolvconf_path; std::string dns_hosts_path; + std::list name_resolvers; //-----------------------[AIO]-------------------------- uint32_t aio_core_worker_num; uint32_t aio_worker_num; +#ifdef SW_USE_IOURING + uint32_t iouring_entries = 0; + uint32_t iouring_workers = 0; + uint32_t iouring_flag = 0; +#endif double aio_max_wait_time; double aio_max_idle_time; network::Socket *aio_default_socket; @@ -673,7 +792,7 @@ struct Global { void *hooks[SW_MAX_HOOK_TYPE]; std::function user_exit_condition; // bug report message - std::string bug_report_message = ""; + std::string bug_report_message; }; std::string dirname(const std::string &file); @@ -683,7 +802,7 @@ double microtime(void); } // namespace swoole extern swoole::Global SwooleG; // Local Global Variable -extern __thread swoole::ThreadGlobal SwooleTG; // Thread Global Variable +extern thread_local swoole::ThreadGlobal SwooleTG; // Thread Global Variable #define SW_CPU_NUM (SwooleG.cpu_num) @@ -691,30 +810,85 @@ static inline void swoole_set_last_error(int error) { SwooleTG.error = error; } -static inline int swoole_get_last_error() { +static inline int swoole_get_last_error(void) { return SwooleTG.error; } -static inline int swoole_get_thread_id() { +static inline void swoole_clear_last_error(void) { + SwooleTG.error = 0; +} + +void swoole_clear_last_error_msg(void); +const char *swoole_get_last_error_msg(void); + +static inline int swoole_get_thread_id(void) { return SwooleTG.id; } -static inline int swoole_get_process_type() { - return SwooleG.process_type; +static inline int swoole_get_thread_type(void) { + return SwooleTG.type; +} + +static inline void swoole_set_thread_id(uint16_t id) { + SwooleTG.id = id; +} + +static inline void swoole_set_thread_type(uint8_t type) { + SwooleTG.type = type; } -static inline int swoole_get_process_id() { +static inline swoole::WorkerId swoole_get_process_id(void) { +#ifdef SW_THREAD + return SwooleTG.process_id; +#else return SwooleG.process_id; +#endif +} + +static inline void swoole_set_process_id(swoole::WorkerId id) { +#ifdef SW_THREAD + SwooleTG.process_id = id; +#else + SwooleG.process_id = id; +#endif +} + +static inline void swoole_set_process_type(int type) { +#ifdef SW_THREAD + SwooleTG.process_type = type; +#else + SwooleG.process_type = type; +#endif +} + +static inline int swoole_get_process_type(void) { +#ifdef SW_THREAD + return SwooleTG.process_type; +#else + return SwooleG.process_type; +#endif +} + +static inline uint32_t swoole_pagesize() { + return SwooleG.pagesize; } SW_API const char *swoole_strerror(int code); SW_API void swoole_throw_error(int code); +SW_API void swoole_ignore_error(int code); +SW_API bool swoole_is_ignored_error(int code); SW_API void swoole_set_log_level(int level); -SW_API void swoole_set_trace_flags(int flags); +SW_API void swoole_set_log_file(const char *file); +SW_API void swoole_set_trace_flags(long flags); SW_API void swoole_set_dns_server(const std::string &server); SW_API void swoole_set_hosts_path(const std::string &hosts_file); SW_API std::pair swoole_get_dns_server(); SW_API bool swoole_load_resolv_conf(); +SW_API void swoole_name_resolver_add(const swoole::NameResolver &resolver, bool append = true); +SW_API void swoole_name_resolver_each( + const std::function::iterator &iter)> &fn); +SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, swoole::NameResolver::Context *ctx); +SW_API int swoole_get_log_level(); //----------------------------------------------- static sw_inline void sw_spinlock(sw_atomic_t *lock) { diff --git a/include/swoole_api.h b/include/swoole_api.h index 827ab46fad0..deacc484ddf 100644 --- a/include/swoole_api.h +++ b/include/swoole_api.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -27,7 +27,13 @@ enum swEventInitFlag { SW_API long swoole_timer_after(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr); SW_API long swoole_timer_tick(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr); -SW_API swoole::TimerNode *swoole_timer_add(long ms, bool persistent, const swoole::TimerCallback &callback, +SW_API swoole::TimerNode *swoole_timer_add(double ms, + bool persistent, + const swoole::TimerCallback &callback, + void *private_data = nullptr); +SW_API swoole::TimerNode *swoole_timer_add(long ms, + bool persistent, + const swoole::TimerCallback &callback, void *private_data = nullptr); SW_API bool swoole_timer_del(swoole::TimerNode *tnode); SW_API bool swoole_timer_exists(long timer_id); @@ -52,6 +58,7 @@ SW_API int swoole_event_free(); SW_API bool swoole_event_set_handler(int fdtype, swoole::ReactorHandler handler); SW_API bool swoole_event_isset_handler(int fdtype); SW_API bool swoole_event_is_available(); +SW_API bool swoole_event_is_running(); #ifdef __MACH__ swoole::Reactor *sw_reactor(); diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index 93214fbc199..7db0fbd9452 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,8 +29,36 @@ SW_EXTERN_C_BEGIN typedef void *fcontext_t; -intptr_t jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); -fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); +struct transfer_t { + fcontext_t fctx; + void * data; +}; + +#ifdef __GNUC__ +#define SW_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#else +#define SW_GCC_VERSION 0 +#endif + +#if defined(__GNUC__) && SW_GCC_VERSION >= 9000 +#define SW_INDIRECT_RETURN __attribute__((__indirect_return__)) +#else +#define SW_INDIRECT_RETURN +#endif + +#undef SWOOLE_CONTEXT_CALLDECL +#if (defined(i386) || defined(__i386__) || defined(__i386) \ + || defined(__i486__) || defined(__i586__) || defined(__i686__) \ + || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) \ + || defined(__I86__) || defined(__INTEL__) || defined(__IA32__) \ + || defined(_M_IX86) || defined(_I86_)) && defined(BOOST_WINDOWS) +# define SWOOLE_CONTEXT_CALLDECL __cdecl +#else +# define SWOOLE_CONTEXT_CALLDECL +#endif + +transfer_t SWOOLE_CONTEXT_CALLDECL swoole_jump_fcontext(fcontext_t const to, void * vp); +fcontext_t SWOOLE_CONTEXT_CALLDECL swoole_make_fcontext(void *stack, size_t stack_size, void (* fn)(transfer_t)); SW_EXTERN_C_END diff --git a/include/swoole_async.h b/include/swoole_async.h index c069a5d588d..c419708e948 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -20,8 +20,6 @@ #include #include -#include -#include #ifndef O_DIRECT #define O_DIRECT 040000 @@ -34,24 +32,22 @@ enum AsyncFlag { SW_AIO_EOF = 1u << 2, }; +struct AsyncRequest { + virtual ~AsyncRequest() = default; +}; + struct AsyncEvent { - int fd; size_t task_id; - uint8_t lock; uint8_t canceled; + int error; /** * input & output */ - uint16_t flags; - off_t offset; - size_t nbytes; - void *buf; - void *req; + std::shared_ptr data; /** * output */ ssize_t retval; - int error; /** * internal use only */ @@ -66,12 +62,52 @@ struct AsyncEvent { } }; +struct GethostbynameRequest : public AsyncRequest { + std::string name; + int family; + char *addr; + size_t addr_len; + + GethostbynameRequest(std::string _name, int _family) : name(std::move(_name)), family(_family) { + addr_len = _family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + addr = new char[addr_len]; + } + + ~GethostbynameRequest() override { + delete[] addr; + } +}; + +struct GetaddrinfoRequest : public AsyncRequest { + std::string hostname; + std::string service; + int family; + int socktype; + int protocol; + int error; + std::vector results; + int count; + + void parse_result(std::vector &retval); + + GetaddrinfoRequest(std::string _hostname, int _family, int _socktype, int _protocol, std::string _service) + : hostname(std::move(_hostname)), + service(std::move(_service)) { + family =_family; + socktype =_socktype; + protocol =_protocol; + count = 0; + error = 0; + } + + ~GetaddrinfoRequest() override = default; +}; + class AsyncThreads { public: - bool schedule = false; size_t task_num = 0; Pipe *pipe = nullptr; - async::ThreadPool *pool = nullptr; + std::shared_ptr pool; network::Socket *read_socket = nullptr; network::Socket *write_socket = nullptr; @@ -87,8 +123,6 @@ class AsyncThreads { void notify_one(); static int callback(Reactor *reactor, Event *event); - private: - std::mutex init_lock; }; namespace async { diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index fa64de55eb2..abf49880182 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -19,32 +19,22 @@ typedef volatile int32_t sw_atomic_int32_t; typedef volatile uint32_t sw_atomic_uint32_t; - -#if defined(__x86_64__) || defined(__aarch64__) typedef volatile int64_t sw_atomic_int64_t; typedef volatile uint64_t sw_atomic_uint64_t; -#endif -#if defined(__x86_64__) || defined(__aarch64__) typedef sw_atomic_int64_t sw_atomic_long_t; typedef sw_atomic_uint64_t sw_atomic_ulong_t; -#else -typedef sw_atomic_int32_t sw_atomic_long_t; -typedef sw_atomic_uint32_t sw_atomic_ulong_t; -#endif - typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_cmp_set(lock, old, set) __sync_bool_compare_and_swap(lock, old, set) +#define sw_atomic_value_cmp_set(value, expected, set) __sync_val_compare_and_swap(value, expected, set) #define sw_atomic_fetch_add(value, add) __sync_fetch_and_add(value, add) #define sw_atomic_fetch_sub(value, sub) __sync_fetch_and_sub(value, sub) #define sw_atomic_memory_barrier() __sync_synchronize() #define sw_atomic_add_fetch(value, add) __sync_add_and_fetch(value, add) #define sw_atomic_sub_fetch(value, sub) __sync_sub_and_fetch(value, sub) -#ifdef __arm__ -#define sw_atomic_cpu_pause() __asm__ __volatile__("NOP"); -#elif defined(__x86_64__) +#if defined(__x86_64__) #define sw_atomic_cpu_pause() __asm__ __volatile__("pause") #elif defined(__aarch64__) #define sw_atomic_cpu_pause() __asm__ __volatile__("yield") @@ -52,8 +42,66 @@ typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_cpu_pause() #endif -#if 0 #define sw_spinlock_release(lock) __sync_lock_release(lock) + +#ifdef HAVE_FUTEX +#include +#include + +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } + + int ret; + struct timespec _timeout; + + if (timeout > 0) { + _timeout.tv_sec = (long) timeout; + _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, NULL, 0); + } else { + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, NULL, NULL, 0); + } + if (ret == 0 && sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } else { + return -1; + } +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (sw_atomic_cmp_set(atomic, 0, 1)) { + return syscall(SYS_futex, atomic, FUTEX_WAKE, n, NULL, NULL, 0); + } else { + return 0; + } +} + #else -#define sw_spinlock_release(lock) *(lock) = 0 +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { + return 0; + } + timeout = timeout <= 0 ? INT_MAX : timeout; + int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); + while (timeout > 0) { + if ((int32_t) *atomic > i) { + return 0; + } else { + usleep(1000); + timeout -= 0.001; + } + } + sw_atomic_fetch_add(atomic, 1); + return -1; +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (1 == (int32_t) *atomic) { + return 0; + } + sw_atomic_fetch_add(atomic, n); + return 0; +} #endif diff --git a/include/swoole_base64.h b/include/swoole_base64.h index 416e75bcde8..eadc13e06d5 100644 --- a/include/swoole_base64.h +++ b/include/swoole_base64.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_buffer.h b/include/swoole_buffer.h index 11b7f3d5d3a..f8339228dc6 100644 --- a/include/swoole_buffer.h +++ b/include/swoole_buffer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,18 +29,20 @@ struct BufferChunk { }; Type type; - uint32_t length; - uint32_t offset; + uint32_t length = 0; + uint32_t offset = 0; union { - char *ptr; - void *object; - struct { - uint32_t val1; - uint32_t val2; - } data; - } value; - uint32_t size; - void (*destroy)(BufferChunk *chunk); + char *str; + void *ptr; + uint32_t u32; + uint64_t u64; + } value{}; + uint32_t size = 0; + + BufferChunk(Type type, uint32_t size); + ~BufferChunk(); + + void (*destroy)(BufferChunk *chunk) = nullptr; }; class Buffer { @@ -51,7 +53,7 @@ class Buffer { std::queue queue_; public: - Buffer(uint32_t chunk_size); + explicit Buffer(uint32_t chunk_size); ~Buffer(); BufferChunk *alloc(BufferChunk::Type type, uint32_t size); @@ -64,15 +66,15 @@ class Buffer { void append(const void *data, uint32_t size); void append(const struct iovec *iov, size_t iovcnt, off_t offset); - uint32_t length() { + uint32_t length() const { return total_length; } - size_t count() { + size_t count() const { return queue_.size(); } - bool empty() { + bool empty() const { return queue_.empty(); } diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h index 88edd144ce8..565e8b2d16f 100644 --- a/include/swoole_c_api.h +++ b/include/swoole_c_api.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -24,6 +24,8 @@ extern "C" { #include #include +#include "swoole_config.h" + enum swGlobalHookType { SW_GLOBAL_HOOK_BEFORE_SERVER_START, SW_GLOBAL_HOOK_BEFORE_CLIENT_START, @@ -37,14 +39,13 @@ enum swGlobalHookType { SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, - SW_GLOBAL_HOOK_END = SW_MAX_HOOK_TYPE - 1, + SW_GLOBAL_HOOK_AFTER_FORK, + SW_GLOBAL_HOOK_USER = 24, + SW_GLOBAL_HOOK_END = SW_MAX_HOOK_TYPE - 1, }; typedef void (*swHookFunc)(void *data); -int swoole_add_function(const char *name, void *func); -void *swoole_get_function(const char *name, uint32_t length); - int swoole_add_hook(enum swGlobalHookType type, swHookFunc cb, int push_back); void swoole_call_hook(enum swGlobalHookType type, void *arg); bool swoole_isset_hook(enum swGlobalHookType type); diff --git a/include/swoole_channel.h b/include/swoole_channel.h index 44d95e6dff6..17a11a6b9aa 100644 --- a/include/swoole_channel.h +++ b/include/swoole_channel.h @@ -51,10 +51,10 @@ struct Channel { Lock *lock; Pipe *notify_pipe; - inline bool empty() { + bool empty() { return num == 0; } - inline bool full() { + bool full() { return ((head == tail && tail_tag != head_tag) || (bytes + sizeof(int) * num == size)); } int pop(void *out_buf, int buffer_length); @@ -66,10 +66,10 @@ struct Channel { int notify(); void destroy(); void print(); - inline int count() { + int count() { return num; } - inline int get_bytes() { + int get_bytes() { return bytes; } static Channel *make(size_t size, size_t maxlen, int flags); diff --git a/include/swoole_client.h b/include/swoole_client.h index 198963a30fa..3031dea6071 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -23,8 +23,6 @@ #include "swoole_protocol.h" #include "swoole_proxy.h" -#define SW_HTTPS_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established" - namespace swoole { namespace network { @@ -105,12 +103,12 @@ class Client { std::shared_ptr ssl_context = nullptr; #endif - std::function onConnect = nullptr; - std::function onError = nullptr; - std::function onReceive = nullptr; - std::function onClose = nullptr; - std::function onBufferFull = nullptr; - std::function onBufferEmpty = nullptr; + std::function onConnect = nullptr; + std::function onError = nullptr; + std::function onReceive = nullptr; + std::function onClose = nullptr; + std::function onBufferFull = nullptr; + std::function onBufferEmpty = nullptr; int (*connect)(Client *cli, const char *host, int port, double _timeout, int sock_flag) = nullptr; ssize_t (*send)(Client *cli, const char *data, size_t length, int flags) = nullptr; @@ -131,6 +129,19 @@ class Client { return socket; } + SocketType get_socket_type() { + return socket->socket_type; + } + + const std::string *get_http_proxy_host_name() { +#ifdef SW_USE_OPENSSL + if (ssl_context && !ssl_context->tls_host_name.empty()) { + return &ssl_context->tls_host_name; + } +#endif + return &http_proxy->target_host; + } + int sleep(); int wakeup(); int shutdown(int __how); @@ -164,7 +175,7 @@ class Stream { int send(const char *data, size_t length); void set_max_length(uint32_t max_length); - inline static Stream *create(const char *dst_host, int dst_port, SocketType type) { + static inline Stream *create(const char *dst_host, int dst_port, SocketType type) { Stream *stream = new Stream(dst_host, dst_port, type); if (!stream->connected) { delete stream; diff --git a/include/swoole_config.h b/include/swoole_config.h index fa300648d8f..cca49d0f301 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #ifndef SWOOLE_CONFIG_H_ @@ -42,7 +42,6 @@ #define SW_MAX_SOCKETS_DEFAULT 1024 #define SW_SOCKET_OVERFLOW_WAIT 100 -#define SW_SOCKET_MAX_DEFAULT 65536 #if defined(__MACH__) || defined(__FreeBSD__) #define SW_SOCKET_BUFFER_SIZE 262144 #else @@ -65,7 +64,6 @@ #define SW_HOST_MAXSIZE \ sizeof(((struct sockaddr_un *) NULL)->sun_path) // Linux has 108 UNIX_PATH_MAX, but BSD/MacOS limit is only 104 -#define SW_LOG_NO_SRCINFO 1 // no source info #define SW_CLIENT_BUFFER_SIZE 65536 #define SW_CLIENT_CONNECT_TIMEOUT 0.5 #define SW_CLIENT_MAX_PORT 65535 @@ -76,6 +74,7 @@ #else #define SW_IPC_MAX_SIZE 8192 // for IPC, dgram and message-queue max size #endif +#define SW_IPC_BUFFER_MAX_SIZE (64 * 1024) #define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swoole::DataHead)) // !!!End.------------------------------------------------------------------- @@ -84,10 +83,6 @@ #define SW_BUFFER_SIZE_UDP 65536 #define SW_SENDFILE_CHUNK_SIZE 65536 -#define SW_SENDFILE_MAXLEN 4194304 - -#define SW_HASHMAP_KEY_MAXLEN 256 -#define SW_HASHMAP_INIT_BUCKET_N 32 // hashmap bucket num (default value for init) #define SW_DATA_EOF "\r\n\r\n" #define SW_DATA_EOF_MAXLEN 8 @@ -97,18 +92,10 @@ #define SW_AIO_THREAD_NUM_MULTIPLE 8 #define SW_AIO_THREAD_MAX_IDLE_TIME 1.0 #define SW_AIO_TASK_MAX_WAIT_TIME 0.001 -#define SW_AIO_MAX_FILESIZE (4 * 1024 * 1024) #define SW_AIO_EVENT_NUM 128 -#define SW_AIO_DEFAULT_CHUNK_SIZE 65536 -#define SW_AIO_MAX_CHUNK_SIZE (1 * 1024 * 1024) -#define SW_AIO_MAX_EVENTS 128 -#define SW_AIO_HANDLER_MAX_SIZE 8 -#define SW_THREADPOOL_QUEUE_LEN 10000 -#define SW_IP_MAX_LENGTH 46 #define SW_WORKER_WAIT_TIMEOUT 1000 -#define SW_WORKER_USE_SIGNALFD 1 #define SW_WORKER_MAX_WAIT_TIME 3 #define SW_WORKER_MIN_REQUEST 10 #define SW_WORKER_MAX_RECV_CHUNK_COUNT 32 @@ -118,7 +105,7 @@ #define SW_SESSION_LIST_SIZE (1 * 1024 * 1024) #define SW_MSGMAX 65536 -#define SW_UNIXSOCK_MAX_BUF_SIZE (2 * 1024 * 1024) +#define SW_MESSAGE_BOX_SIZE 65536 #define SW_DGRAM_HEADER_SIZE 32 @@ -129,24 +116,6 @@ */ #define SW_REACTOR_MAX_THREAD 8 -/** - * Loops read data from the pipeline, - * helping to alleviate pipeline cache congestion - * reduce the pressure of interprocess communication - */ -#define SW_REACTOR_RECV_AGAIN 1 - -/** - * RINGBUFFER - */ -#define SW_RINGQUEUE_LEN 1024 -#define SW_RINGBUFFER_FREE_N_MAX 4 // when free_n > MAX, execute collect -#define SW_RINGBUFFER_WARNING 100 - -/** - * ringbuffer memory pool size - */ -#define SW_OUTPUT_BUFFER_SIZE (2 * 1024 * 1024) #define SW_INPUT_BUFFER_SIZE (2 * 1024 * 1024) #define SW_BUFFER_MIN_SIZE 65536 #define SW_SEND_BUFFER_SIZE 65536 @@ -163,11 +132,8 @@ #define SW_TCP_KEEPIDLE 3600 // 1 hour #define SW_TCP_KEEPINTERVAL 60 -#define SW_USE_EVENTFD \ - 1 // Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support - -#define SW_TASK_TMP_PATH_SIZE 256 -#define SW_TASK_TMP_DIR "/tmp" +#define SW_TASK_TMP_PATH_SIZE 256 +#define SW_TASK_TMP_DIR "/tmp" #define SW_TASK_TMP_FILE "swoole.task.XXXXXX" #define SW_FILE_CHUNK_SIZE 65536 @@ -181,11 +147,6 @@ #define SW_SPINLOCK_LOOP_N 1024 -#define SW_STRING_BUFFER_MAXLEN (1024 * 1024 * 128) -#define SW_STRING_BUFFER_DEFAULT 128 -#define SW_STRING_BUFFER_GARBAGE_MIN (1024 * 64) -#define SW_STRING_BUFFER_GARBAGE_RATIO 4 - #define SW_SIGNO_MAX 128 #define SW_UNREGISTERED_SIGNAL_FMT "Unable to find callback function for signal %s" @@ -194,22 +155,25 @@ #define SW_DNS_RESOLV_CONF "/etc/resolv.conf" #define SW_Z_BEST_SPEED 1 -#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20 +#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20 #ifndef IOV_MAX -#define IOV_MAX 16 +#define IOV_MAX 16 #endif -#define IOV_MAX_ERROR_MSG "The maximum of iov count is %d" +#define SW_IOV_MAX_ERROR_MSG "The maximum of iov count is %d" + +#define SW_IOURING_CQES_SIZE 8192 /** * HTTP Protocol */ #define SW_HTTP_SERVER_SOFTWARE "swoole-http-server" +#define SW_HTTP_SERVER_BOUNDARY_PREKEY "SwooleBoundary" +#define SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE 39 +#define SW_HTTP_SERVER_PART_HEADER 256 #define SW_HTTP_PARAM_MAX_NUM 128 #define SW_HTTP_FORM_KEYLEN 512 -#define SW_HTTP_COOKIE_KEYLEN 128 -#define SW_HTTP_COOKIE_VALLEN 4096 #define SW_HTTP_RESPONSE_INIT_SIZE 65536 #define SW_HTTP_HEADER_MAX_SIZE 65536 #define SW_HTTP_HEADER_KEY_SIZE 128 @@ -219,26 +183,44 @@ #define SW_HTTP_RFC1123_DATE_UTC "%a, %d %b %Y %T UTC" #define SW_HTTP_RFC850_DATE "%A, %d-%b-%y %T GMT" #define SW_HTTP_ASCTIME_DATE "%a %b %e %T %Y" +#define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" +#define SW_HTTP_CHUNK_EOF "0\r\n\r\n" +#define SW_HTTP_DEFAULT_CONTENT_TYPE "text/html" +#define SW_HTTP_MAX_APPEND_DATA 16384 // #define SW_HTTP_100_CONTINUE #define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" #define SW_HTTP_BAD_REQUEST_PACKET "HTTP/1.1 400 Bad Request\r\n\r\n" #define SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET "HTTP/1.1 413 Request Entity Too Large\r\n\r\n" #define SW_HTTP_SERVICE_UNAVAILABLE_PACKET "HTTP/1.1 503 Service Unavailable\r\n\r\n" -#define SW_HTTP_PAGE_400 "

HTTP 400 Bad Request


Powered by Swoole" -#define SW_HTTP_PAGE_404 "

HTTP 404 Not Found


Powered by Swoole" + +#define SW_HTTP_PAGE_CSS \ + "" + +#define SW_HTTP_POWER_BY "
Powered by Swoole
" + +#define SW_HTTP_PAGE_400 \ + "" SW_HTTP_PAGE_CSS "

HTTP 400 Bad Request

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_404 "" SW_HTTP_PAGE_CSS "

HTTP 404 Not Found

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_500 \ + "" SW_HTTP_PAGE_CSS "

HTTP 500 Internal Server Error

" SW_HTTP_POWER_BY "" /** * HTTP2 Protocol */ #define SW_HTTP2_DATA_BUFFER_SIZE 8192 #define SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) -#define SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS 128 -#define SW_HTTP2_MAX_MAX_FRAME_SIZE ((1u << 14)) -#define SW_HTTP2_MAX_WINDOW_SIZE ((1u << 31) - 1) -#define SW_HTTP2_DEFAULT_WINDOW_SIZE 65535 -#define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE (1 << 12) -#define SW_HTTP2_MAX_MAX_HEADER_LIST_SIZE UINT32_MAX +#define SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS UINT_MAX +#define SW_HTTP2_DEFAULT_ENABLE_PUSH 0 +#define SW_HTTP2_DEFAULT_MAX_FRAME_SIZE (1u << 14) +#define SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE ((1 << 16) - 1) +#define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE UINT_MAX #define SW_HTTP_CLIENT_USERAGENT "swoole-http-client" #define SW_HTTP_CLIENT_BOUNDARY_PREKEY "----SwooleBoundary" @@ -270,8 +252,7 @@ * Coroutine */ #define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024) -#define SW_CORO_SUPPORT_BAILOUT 1 -#define SW_CORO_SWAP_BAILOUT 1 +#define SW_CORO_BAILOUT_EXIT_CODE 1 //#define SW_CONTEXT_PROTECT_STACK_PAGE 1 //#define SW_CONTEXT_DETECT_STACK_USAGE 1 diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 93997f838a6..3749fd65bb9 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -23,14 +23,26 @@ #include "swoole_reactor.h" #include "swoole_timer.h" #include "swoole_async.h" +#include "swoole_util.h" #include "swoole_coroutine_context.h" -#include +#include #include #include #include +#ifdef SW_USE_THREAD_CONTEXT +#include +#endif + +typedef std::chrono::microseconds seconds_type; + +#ifdef SW_CORO_TIME +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) calc_execute_usec(yield_coroutine, resume_coroutine) +#else +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) +#endif namespace swoole { class Coroutine { @@ -60,8 +72,8 @@ class Coroutine { }; typedef void (*SwapCallback)(void *); - typedef void (*BailoutCallback)(); - typedef std::function CancelFunc; + typedef std::function BailoutCallback; + typedef std::function CancelFunc; void resume(); void yield(); @@ -70,31 +82,31 @@ class Coroutine { bool yield_ex(double timeout = -1); - inline enum State get_state() const { + enum State get_state() const { return state; } - inline long get_init_msec() const { + long get_init_msec() const { return init_msec; } - inline long get_cid() const { + long get_cid() const { return cid; } - inline Coroutine *get_origin() { + Coroutine *get_origin() { return origin; } - inline long get_origin_cid() { + long get_origin_cid() { return sw_likely(origin) ? origin->get_cid() : -1; } - inline void *get_task() { + void *get_task() { return task; } - inline bool is_end() { + bool is_end() { return ctx.is_end(); } @@ -110,7 +122,7 @@ class Coroutine { return state == STATE_WAITING; } - inline void set_task(void *_task) { + void set_task(void *_task) { task = _task; } @@ -118,18 +130,33 @@ class Coroutine { cancel_fn_ = cancel_fn; } - static std::unordered_map coroutines; + long get_execute_usec() const { + return time(true) - switch_usec + execute_usec; + } + + coroutine::Context &get_ctx() { + return ctx; + } + + static SW_THREAD_LOCAL std::unordered_map coroutines; static void set_on_yield(SwapCallback func); static void set_on_resume(SwapCallback func); static void set_on_close(SwapCallback func); static void bailout(BailoutCallback func); + static inline bool run(const CoroutineFunc &fn, void *args = nullptr) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + long cid = create(fn, args); + swoole_event_wait(); + return cid > 0; + } + static inline long create(const CoroutineFunc &fn, void *args = nullptr) { #ifdef SW_USE_THREAD_CONTEXT try { return (new Coroutine(fn, args))->run(); - } catch (const std::system_error& e) { + } catch (const std::system_error &e) { swoole_set_last_error(e.code().value()); swoole_warning("failed to create coroutine, Error: %s[%d]", e.what(), swoole_get_last_error()); return -1; @@ -196,53 +223,78 @@ class Coroutine { return sw_likely(co) ? Timer::get_absolute_msec() - co->get_init_msec() : -1; } + static inline long get_execute_time(long cid) { + Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid); + return sw_likely(co) ? co->get_execute_usec() : -1; + } + + static inline void calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine) { + long current_usec = time(true); + if (yield_coroutine) { + yield_coroutine->execute_usec += current_usec - yield_coroutine->switch_usec; + } + + if (resume_coroutine) { + resume_coroutine->switch_usec = current_usec; + } + } + static void print_list(); protected: - static Coroutine *current; - static long last_cid; - static uint64_t peak_num; - static size_t stack_size; - static SwapCallback on_yield; /* before yield */ - static SwapCallback on_resume; /* before resume */ - static SwapCallback on_close; /* before close */ - static BailoutCallback on_bailout; /* when bailout */ - static bool activated; + static SW_THREAD_LOCAL Coroutine *current; + static SW_THREAD_LOCAL long last_cid; + static SW_THREAD_LOCAL uint64_t peak_num; + static SW_THREAD_LOCAL size_t stack_size; + static SW_THREAD_LOCAL SwapCallback on_yield; /* before yield */ + static SW_THREAD_LOCAL SwapCallback on_resume; /* before resume */ + static SW_THREAD_LOCAL SwapCallback on_close; /* before close */ + static SW_THREAD_LOCAL BailoutCallback on_bailout; /* when bailout */ + static SW_THREAD_LOCAL bool activated; enum State state = STATE_INIT; enum ResumeCode resume_code_ = RC_OK; long cid; long init_msec = Timer::get_absolute_msec(); + long switch_usec = time(true); + long execute_usec = 0; void *task = nullptr; coroutine::Context ctx; Coroutine *origin = nullptr; CancelFunc *cancel_fn_ = nullptr; - + Coroutine(const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { cid = ++last_cid; coroutines[cid] = this; if (sw_unlikely(count() > peak_num)) { peak_num = count(); } + if (!activated) { + activate(); + } + } + + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { + cid = _cid; } - inline long run() { + long run() { long cid = this->cid; origin = current; current = this; + CALC_EXECUTE_USEC(origin, nullptr); + state = STATE_RUNNING; ctx.swap_in(); check_end(); return cid; } - inline void check_end() { + void check_end() { if (ctx.is_end()) { close(); } else if (sw_unlikely(on_bailout)) { SW_ASSERT(current == nullptr); on_bailout(); - // expect that never here - exit(1); } } @@ -250,9 +302,25 @@ class Coroutine { }; //------------------------------------------------------------------------------- namespace coroutine { +/** + * Support for timeouts and cancellations requires the caller to store the memory pointers of + * the input and output parameter objects in the `data` pointer of the `AsyncEvent` object. + * This field is a `shared_ptr`, which increments the reference count when dispatched to the AIO thread, + * collectively managing the `data` pointer. + * When the async task is completed, the caller receives the results or cancels or timeouts, + * the reference count will reach zero, and the memory will be released. + */ bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); -bool async(const std::function &fn, double timeout = -1); +/** + * This function should be used for asynchronous operations that do not support cancellation and timeouts. + * For example, in write/read operations, + * asynchronous tasks cannot transfer the memory ownership of wbuf/rbuf to the AIO thread. + * In the event of a timeout or cancellation, the memory of wbuf/rbuf will be released by the caller, + * which may lead the AIO thread to read from an erroneous memory pointer and consequently crash. + */ +bool async(const std::function &fn); bool run(const CoroutineFunc &fn, void *arg = nullptr); +bool wait_for(const std::function &fn); } // namespace coroutine //------------------------------------------------------------------------------- } // namespace swoole diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 8c3b51ea982..10711d273b3 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -32,11 +32,15 @@ extern "C" { #include #include +#ifdef __APPLE__ +extern int fdatasync(int); +#endif + /** * base */ -uint8_t swoole_coroutine_is_in(); -long swoole_coroutine_get_current_id(); +uint8_t swoole_coroutine_is_in(void); +long swoole_coroutine_get_current_id(void); void swoole_coroutine_sleep(int sec); void swoole_coroutine_usleep(int usec); /** @@ -48,6 +52,8 @@ ssize_t swoole_coroutine_read(int fd, void *buf, size_t count); ssize_t swoole_coroutine_write(int fd, const void *buf, size_t count); off_t swoole_coroutine_lseek(int fd, off_t offset, int whence); int swoole_coroutine_fstat(int fd, struct stat *statbuf); +int swoole_coroutine_stat(const char *path, struct stat *statbuf); +int swoole_coroutine_lstat(const char *path, struct stat *statbuf); int swoole_coroutine_readlink(const char *pathname, char *buf, size_t len); int swoole_coroutine_unlink(const char *pathname); int swoole_coroutine_mkdir(const char *pathname, mode_t mode); @@ -56,6 +62,29 @@ int swoole_coroutine_rename(const char *oldpath, const char *newpath); int swoole_coroutine_flock(int fd, int operation); int swoole_coroutine_flock_ex(const char *filename, int fd, int operation); int swoole_coroutine_statvfs(const char *path, struct statvfs *buf); +int swoole_coroutine_close_file(int fd); +int swoole_coroutine_fsync(int fd); +int swoole_coroutine_fdatasync(int fd); +/** + * io_uring + */ +#ifdef SW_USE_IOURING +int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode); +int swoole_coroutine_iouring_close_file(int fd); +ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count); +ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count); +int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath); +int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode); +int swoole_coroutine_iouring_unlink(const char *pathname); +#ifdef HAVE_IOURING_STATX +int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf); +int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf); +int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf); +#endif +int swoole_coroutine_iouring_rmdir(const char *pathname); +int swoole_coroutine_iouring_fsync(int fd); +int swoole_coroutine_iouring_fdatasync(int fd); +#endif /** * stdio */ @@ -79,6 +108,7 @@ int swoole_coroutine_closedir(DIR *dirp); */ int swoole_coroutine_socket(int domain, int type, int protocol); int swoole_coroutine_socket_create(int fd); +int swoole_coroutine_socket_unwrap(int fd); uint8_t swoole_coroutine_socket_exists(int fd); ssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags); ssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags); @@ -87,6 +117,7 @@ ssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags); int swoole_coroutine_close(int fd); int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout); +int swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout); int swoole_coroutine_socket_set_timeout(int fd, int which, double timeout); int swoole_coroutine_socket_set_connect_timeout(int fd, double timeout); int swoole_coroutine_socket_wait_event(int fd, int event, double timeout); @@ -98,7 +129,7 @@ struct hostent *swoole_coroutine_gethostbyname(const char *name); /** * wait */ -size_t swoole_coroutine_wait_count(); +size_t swoole_coroutine_wait_count(void); pid_t swoole_coroutine_waitpid(pid_t __pid, int *__stat_loc, int __options); pid_t swoole_coroutine_wait(int *__stat_loc); diff --git a/include/swoole_coroutine_channel.h b/include/swoole_coroutine_channel.h index 6ba1315acf4..0624e9aac13 100644 --- a/include/swoole_coroutine_channel.h +++ b/include/swoole_coroutine_channel.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -73,31 +73,31 @@ class Channel { } } - inline bool is_closed() { + bool is_closed() { return closed; } - inline bool is_empty() { + bool is_empty() { return data_queue.size() == 0; } - inline bool is_full() { + bool is_full() { return data_queue.size() == capacity; } - inline size_t length() { + size_t length() { return data_queue.size(); } - inline size_t consumer_num() { + size_t consumer_num() { return consumer_queue.size(); } - inline size_t producer_num() { + size_t producer_num() { return producer_queue.size(); } - inline void *pop_data() { + void *pop_data() { if (data_queue.size() == 0) { return nullptr; } @@ -122,15 +122,15 @@ class Channel { void yield(enum Opcode type); - inline void consumer_remove(Coroutine *co) { + void consumer_remove(Coroutine *co) { consumer_queue.remove(co); } - inline void producer_remove(Coroutine *co) { + void producer_remove(Coroutine *co) { producer_queue.remove(co); } - inline Coroutine *pop_coroutine(enum Opcode type) { + Coroutine *pop_coroutine(enum Opcode type) { Coroutine *co; if (type == PRODUCER) { co = producer_queue.front(); diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index 6ff33f35666..0dffd6f326c 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -42,6 +42,11 @@ typedef ucontext_t coroutine_context_t; #elif defined(USE_ASM_CONTEXT) typedef fcontext_t coroutine_context_t; +typedef transfer_t coroutine_transfer_t; +#endif + +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) +typedef void * coroutine_transfer_t; #endif typedef std::function CoroutineFunc; @@ -51,16 +56,26 @@ namespace coroutine { class Context { public: - Context(size_t stack_size, const CoroutineFunc &fn, void *private_data); + Context(size_t stack_size, CoroutineFunc fn, void *private_data); ~Context(); bool swap_in(); bool swap_out(); #if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) ssize_t get_stack_usage(); #endif - inline bool is_end() { +#ifndef SW_USE_THREAD_CONTEXT + char *get_stack() const { + return stack_; + } + + size_t get_stack_size() const { + return stack_size_; + } +#endif + bool is_end() const { return end_; } + protected: CoroutineFunc fn_; #ifdef SW_USE_THREAD_CONTEXT @@ -79,7 +94,7 @@ class Context { void *private_data_; bool end_; - static void context_func(void *arg); + static void context_func(coroutine_transfer_t arg); }; } // namespace coroutine diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index b6c71695cb8..f5eff4a307a 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -65,14 +65,22 @@ class Socket { Socket(SocketType type = SW_SOCK_TCP); Socket(int _fd, SocketType _type); ~Socket(); - bool connect(std::string host, int port, int flags = 0); + bool connect(std::string host, int port = 0, int flags = 0); bool connect(const struct sockaddr *addr, socklen_t addrlen); bool shutdown(int how = SHUT_RDWR); bool cancel(const EventType event); bool close(); - inline bool is_connected() { - return connected && !closed; + bool is_connected() { + return connected && !is_closed(); + } + + bool is_closed() { + return sock_fd == SW_BAD_SOCKET; + } + + bool is_port_required() { + return type <= SW_SOCK_UDP6; } bool check_liveness(); @@ -80,7 +88,7 @@ class Socket { ssize_t recv(void *__buf, size_t __n); ssize_t send(const void *__buf, size_t __n); - inline ssize_t send(const std::string &buf) { + ssize_t send(const std::string &buf) { return send(buf.c_str(), buf.length()); } @@ -98,7 +106,7 @@ class Socket { ssize_t recv_line(void *__buf, size_t maxlen); ssize_t recv_with_buffer(void *__buf, size_t __n); - inline char *pop_packet() { + char *pop_packet() { if (read_buffer->offset == 0) { return nullptr; } else { @@ -106,7 +114,7 @@ class Socket { } } - bool poll(EventType type); + bool poll(EventType type, double timeout = 0); Socket *accept(double timeout = 0); bool bind(std::string address, int port = 0); bool bind(const struct sockaddr *sa, socklen_t len); @@ -121,8 +129,7 @@ class Socket { * Operation sequence: * 1. enable_ssl_encrypt() * 2. Set SSL parameters, such as certificate file, key file - * 3. ssl_check_context() - * 4. ssl_accept()/ssl_connect()/ssl_handshake() + * 3. ssl_handshake(), to be executed after connect or accept */ bool enable_ssl_encrypt() { if (ssl_context.get()) { @@ -140,7 +147,6 @@ class Socket { return ssl_context.get(); } - bool ssl_check_context(); bool ssl_handshake(); bool ssl_verify(bool allow_self_signed); std::string ssl_get_peer_cert(); @@ -152,54 +158,54 @@ class Socket { reactor->set_handler(SW_FD_CO_SOCKET | SW_EVENT_ERROR, error_event_callback); } - inline SocketType get_type() { + SocketType get_type() { return type; } - inline FdType get_fd_type() { + FdType get_fd_type() { return socket->fd_type; } - inline int get_sock_domain() { + int get_sock_domain() { return sock_domain; } - inline int get_sock_type() { + int get_sock_type() { return sock_type; } - inline int get_sock_protocol() { + int get_sock_protocol() { return sock_protocol; } - inline int get_fd() { + int get_fd() { return sock_fd; } - inline int get_bind_port() { + int get_bind_port() { return bind_port; } - inline network::Socket *get_socket() { + network::Socket *get_socket() { return socket; } bool getsockname(network::Address *sa); bool getpeername(network::Address *sa); - inline const char *get_ip() { + const char *get_ip() { return socket->info.get_ip(); } - inline int get_port() { + int get_port() { return socket->info.get_port(); } - inline bool has_bound(const EventType event = SW_EVENT_RDWR) { + bool has_bound(const EventType event = SW_EVENT_RDWR) { return get_bound_co(event) != nullptr; } - inline Coroutine *get_bound_co(const EventType event) { + Coroutine *get_bound_co(const EventType event) { if (event & SW_EVENT_READ) { if (read_co) { return read_co; @@ -213,7 +219,7 @@ class Socket { return nullptr; } - inline long get_bound_cid(const EventType event = SW_EVENT_RDWR) { + long get_bound_cid(const EventType event = SW_EVENT_RDWR) { Coroutine *co = get_bound_co(event); return co ? co->get_cid() : 0; } @@ -228,32 +234,32 @@ class Socket { } } - inline void check_bound_co(const EventType event) { + void check_bound_co(const EventType event) { long cid = get_bound_cid(event); if (sw_unlikely(cid)) { swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, - "Socket#%d has already been bound to another coroutine#%ld, " - "%s of the same socket in coroutine#%ld at the same time is not allowed", - sock_fd, - cid, - get_event_str(event), - Coroutine::get_current_cid()); + "Socket#%d has already been bound to another coroutine#%ld, " + "%s of the same socket in coroutine#%ld at the same time is not allowed", + sock_fd, + cid, + get_event_str(event), + Coroutine::get_current_cid()); } } - inline void set_err(int e) { + void set_err(int e) { errCode = errno = e; swoole_set_last_error(errCode); errMsg = e ? swoole_strerror(e) : ""; } - inline void set_err(int e, const char *s) { + void set_err(int e, const char *s) { errCode = errno = e; swoole_set_last_error(errCode); errMsg = s; } - inline void set_err(int e, std::string s) { + void set_err(int e, std::string s) { errCode = errno = e; swoole_set_last_error(errCode); errString = s; @@ -261,7 +267,7 @@ class Socket { } /* set connect read write timeout */ - inline void set_timeout(double timeout, int type = TIMEOUT_ALL) { + void set_timeout(double timeout, int type = TIMEOUT_ALL) { if (timeout == 0) { return; } @@ -279,11 +285,11 @@ class Socket { } } - inline void set_timeout(struct timeval *timeout, int type = TIMEOUT_ALL) { + void set_timeout(struct timeval *timeout, int type = TIMEOUT_ALL) { set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), type); } - inline double get_timeout(enum TimeoutType type = TIMEOUT_ALL) { + double get_timeout(enum TimeoutType type = TIMEOUT_ALL) { SW_ASSERT_1BYTE(type); if (type == TIMEOUT_DNS) { return dns_timeout; @@ -299,7 +305,7 @@ class Socket { } } - inline bool set_option(int level, int optname, int optval) { + bool set_option(int level, int optname, int optval) { if (socket->set_option(level, optname, optval) < 0) { swoole_sys_warning("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); return false; @@ -307,7 +313,7 @@ class Socket { return true; } - inline String *get_read_buffer() { + String *get_read_buffer() { if (sw_unlikely(!read_buffer)) { read_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); if (!read_buffer) { @@ -317,7 +323,7 @@ class Socket { return read_buffer; } - inline String *get_write_buffer() { + String *get_write_buffer() { if (sw_unlikely(!write_buffer)) { write_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); if (!write_buffer) { @@ -327,7 +333,15 @@ class Socket { return write_buffer; } - inline String *pop_read_buffer() { + void set_resolve_context(NameResolver::Context *ctx) { + resolve_context_ = ctx; + } + + void set_dtor(const std::function &dtor) { + dtor_ = dtor; + } + + String *pop_read_buffer() { if (sw_unlikely(!read_buffer)) { return nullptr; } @@ -336,7 +350,7 @@ class Socket { return tmp; } - inline String *pop_write_buffer() { + String *pop_write_buffer() { if (sw_unlikely(!write_buffer)) { return nullptr; } @@ -345,15 +359,15 @@ class Socket { return tmp; } - inline void set_zero_copy(bool enable) { + void set_zero_copy(bool enable) { zero_copy = enable; } - inline void set_buffer_allocator(const Allocator *allocator) { + void set_buffer_allocator(const Allocator *allocator) { buffer_allocator = allocator; } - inline void set_buffer_init_size(size_t size) { + void set_buffer_init_size(size_t size) { if (size == 0) { return; } @@ -361,9 +375,8 @@ class Socket { } int move_fd() { - int sockfd = socket->fd; - socket->fd = -1; - return sockfd; + sock_fd = SW_BAD_SOCKET; + return socket->move_fd(); } network::Socket *move_socket() { @@ -373,7 +386,7 @@ class Socket { } #ifdef SW_USE_OPENSSL - inline bool ssl_is_available() { + bool ssl_is_available() { return socket && ssl_handshaked; } @@ -426,16 +439,19 @@ class Socket { bool ssl_handshaked = false; std::shared_ptr ssl_context = nullptr; std::string ssl_host_name; + bool ssl_context_create(); bool ssl_create(SSLContext *ssl_context); #endif bool connected = false; bool shutdown_read = false; bool shutdown_write = false; - bool closed = false; bool zero_copy = false; + NameResolver::Context *resolve_context_ = nullptr; + std::function dtor_; + Socket(network::Socket *sock, Socket *socket); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -443,8 +459,8 @@ class Socket { static int writable_event_callback(Reactor *reactor, Event *event); static int error_event_callback(Reactor *reactor, Event *event); - inline void init_sock_type(SocketType _type); - inline bool init_sock(); + void init_sock_type(SocketType _type); + bool init_sock(); bool init_reactor_socket(int fd); void check_return_value(ssize_t retval) { @@ -455,7 +471,7 @@ class Socket { } } - inline void init_options() { + void init_options() { if (type == SW_SOCK_TCP || type == SW_SOCK_TCP6) { set_option(IPPROTO_TCP, TCP_NODELAY, 1); } @@ -468,16 +484,21 @@ class Socket { bool add_event(const EventType event); bool wait_event(const EventType event, const void **__buf = nullptr, size_t __n = 0); + bool try_connect(); ssize_t recv_packet_with_length_protocol(); ssize_t recv_packet_with_eof_protocol(); - inline bool is_available(const EventType event) { + bool is_available(const EventType event) { if (event != SW_EVENT_NULL) { check_bound_co(event); } - if (sw_unlikely(closed)) { - set_err(ECONNRESET); + if (sw_unlikely(is_closed())) { + set_err(EBADF); + return false; + } + if (sw_unlikely(socket->close_wait)) { + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); return false; } return true; @@ -488,13 +509,13 @@ class Socket { class TimerController { public: - TimerController(TimerNode **timer_pp, double timeout, Socket *sock, TimerCallback callback) - : timer_pp(timer_pp), timeout(timeout), socket_(sock), callback(callback) {} + TimerController(TimerNode **_timer_pp, double _timeout, Socket *_socket, TimerCallback _callback) + : timer_pp(_timer_pp), timeout(_timeout), socket_(_socket), callback(std::move(_callback)) {} bool start() { if (timeout != 0 && !*timer_pp) { enabled = true; if (timeout > 0) { - *timer_pp = swoole_timer_add((long) (timeout * 1000), false, callback, socket_); + *timer_pp = swoole_timer_add(timeout, false, callback, socket_); return *timer_pp != nullptr; } *timer_pp = (TimerNode *) -1; @@ -521,16 +542,16 @@ class Socket { public: class TimeoutSetter { public: - TimeoutSetter(Socket *socket, double timeout, const enum TimeoutType type) - : socket_(socket), timeout(timeout), type(type) { - if (timeout == 0) { + TimeoutSetter(Socket *socket, double _timeout, const enum TimeoutType _type) + : socket_(socket), timeout(_timeout), type(_type) { + if (_timeout == 0) { return; } for (uint8_t i = 0; i < SW_ARRAY_SIZE(timeout_type_list); i++) { - if (type & timeout_type_list[i]) { + if (_type & timeout_type_list[i]) { original_timeout[i] = socket->get_timeout(timeout_type_list[i]); - if (timeout != original_timeout[i]) { - socket->set_timeout(timeout, timeout_type_list[i]); + if (_timeout != original_timeout[i]) { + socket->set_timeout(_timeout, timeout_type_list[i]); } } } @@ -555,12 +576,13 @@ class Socket { double original_timeout[sizeof(timeout_type_list)] = {}; }; - class timeout_controller : public TimeoutSetter { + class TimeoutController : public TimeoutSetter { public: - timeout_controller(Socket *socket, double timeout, const enum TimeoutType type) - : TimeoutSetter(socket, timeout, type) {} - inline bool has_timedout(const enum TimeoutType type) { - SW_ASSERT_1BYTE(type); + TimeoutController(Socket *_socket, double _timeout, const enum TimeoutType _type) + : TimeoutSetter(_socket, _timeout, _type) {} + + bool has_timedout(const enum TimeoutType _type) { + SW_ASSERT_1BYTE(_type); if (timeout > 0) { if (sw_unlikely(startup_time == 0)) { startup_time = microtime(); @@ -570,7 +592,7 @@ class Socket { socket_->set_err(ETIMEDOUT); return true; } - socket_->set_timeout(timeout - used_time, type); + socket_->set_timeout(timeout - used_time, _type); } } return false; @@ -614,4 +636,4 @@ std::string get_ip_by_hosts(const std::string &domain); } // namespace coroutine } // namespace swoole -swoole::coroutine::Socket *swoole_coroutine_get_socket_object(int sockfd); +std::shared_ptr swoole_coroutine_get_socket_object(int sockfd); diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index fefaa4aeb8f..e55d9913ec3 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -39,6 +39,9 @@ struct PollSocket { } }; +int translate_events_to_poll(int events); +int translate_events_from_poll(int events); + class System { public: static void init_reactor(Reactor *reactor); @@ -63,12 +66,30 @@ class System { /* wait */ static pid_t wait(int *__stat_loc, double timeout = -1); static pid_t waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout = -1); + /** + * waitpid_safe() does not deps on the signal + * and can be safely used in a multi-threaded environment. + */ + static pid_t waitpid_safe(pid_t __pid, int *__stat_loc, int __options); /* signal */ - static bool wait_signal(int signo, double timeout = -1); + static int wait_signal(int signal, double timeout = -1); + static int wait_signal(const std::vector &signals, double timeout = -1); /* event */ static int wait_event(int fd, int events, double timeout); + static bool exec(const char *command, bool get_error_stream, std::shared_ptr buffer, int *status); }; std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout = -1); //------------------------------------------------------------------------------- +struct AsyncLock { + private: + void *resource_; + + public: + AsyncLock(void *resource); + ~AsyncLock(); +}; + +std::shared_ptr async_lock(void *); +//------------------------------------------------------------------------------- } // namespace coroutine } // namespace swoole diff --git a/include/swoole_dtls.h b/include/swoole_dtls.h index b8b34f8a287..4a7046babd5 100644 --- a/include/swoole_dtls.h +++ b/include/swoole_dtls.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -65,11 +65,11 @@ struct Session { void append(const char *data, ssize_t len); - inline void append(Buffer *buffer) { + void append(Buffer *buffer) { rxqueue.push_back(buffer); } - inline size_t get_buffer_length() { + size_t get_buffer_length() { size_t total_length = 0; for (auto i : rxqueue) { total_length += i->length; diff --git a/include/swoole_error.h b/include/swoole_error.h index 6992edbeec6..48862ebf295 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -34,6 +34,13 @@ enum swErrorCode { SW_ERROR_OPERATION_NOT_SUPPORT, SW_ERROR_PROTOCOL_ERROR, SW_ERROR_WRONG_OPERATION, + SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings + SW_ERROR_FOR_TEST, + + SW_ERROR_NO_PAYLOAD = 550, + + SW_ERROR_UNDEFINED_BEHAVIOR = 600, + SW_ERROR_NOT_THREAD_SAFETY, SW_ERROR_FILE_NOT_EXIST = 700, SW_ERROR_FILE_TOO_LARGE, @@ -47,6 +54,7 @@ enum swErrorCode { SW_ERROR_BAD_IPV6_ADDRESS = 720, SW_ERROR_UNREGISTERED_SIGNAL, + SW_ERROR_BAD_HOST_ADDR, // EventLoop SW_ERROR_EVENT_SOCKET_REMOVED = 800, @@ -72,10 +80,12 @@ enum swErrorCode { SW_ERROR_SSL_BAD_PROTOCOL, SW_ERROR_SSL_RESET, SW_ERROR_SSL_HANDSHAKE_FAILED, + SW_ERROR_SSL_CREATE_CONTEXT_FAILED, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE = 1201, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, SW_ERROR_DATA_LENGTH_TOO_LARGE, + SW_ERROR_PACKAGE_MALFORMED_DATA, /** * task error @@ -91,6 +101,7 @@ enum swErrorCode { SW_ERROR_HTTP2_STREAM_NO_HEADER, SW_ERROR_HTTP2_STREAM_NOT_FOUND, SW_ERROR_HTTP2_STREAM_IGNORE, + SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED, /** * AIO @@ -123,12 +134,17 @@ enum swErrorCode { SW_ERROR_HTTP_INVALID_PROTOCOL, SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED, SW_ERROR_HTTP_PROXY_BAD_RESPONSE, + SW_ERROR_HTTP_CONFLICT_HEADER, + SW_ERROR_HTTP_CONTEXT_UNAVAILABLE, + SW_ERROR_HTTP_COOKIE_UNAVAILABLE, SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501, SW_ERROR_WEBSOCKET_BAD_OPCODE, SW_ERROR_WEBSOCKET_UNCONNECTED, SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED, SW_ERROR_WEBSOCKET_PACK_FAILED, + SW_ERROR_WEBSOCKET_UNPACK_FAILED, + SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET, /** * server global error @@ -145,6 +161,10 @@ enum swErrorCode { SW_ERROR_SERVER_INVALID_REQUEST, SW_ERROR_SERVER_CONNECT_FAIL, SW_ERROR_SERVER_INVALID_COMMAND, + SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, + SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, + SW_ERROR_SERVER_INVALID_CALLBACK, + SW_ERROR_SERVER_UNRELATED_THREAD, /** * Process exit timeout, forced to end. @@ -178,6 +198,10 @@ enum swErrorCode { SW_ERROR_CO_CANCELED, SW_ERROR_CO_TIMEDOUT, + // close failed, there are currently other coroutines holding this socket, + // need to wait for the bound coroutine to return from the socket wait_event operation + SW_ERROR_CO_SOCKET_CLOSE_WAIT, + SW_ERROR_END }; diff --git a/include/swoole_file.h b/include/swoole_file.h index dd7a2bb75e3..bca2711f0d3 100644 --- a/include/swoole_file.h +++ b/include/swoole_file.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -30,6 +30,7 @@ ssize_t file_get_size(int fd); ssize_t file_get_size(const std::string &filename); std::shared_ptr file_get_contents(const std::string &filename); bool file_put_contents(const std::string &filename, const char *content, size_t length); +bool file_exists(const std::string &filename); typedef struct stat FileStatus; @@ -100,6 +101,14 @@ class File { size_t write_all(const void *__buf, size_t __n); size_t read_all(void *__buf, size_t __n); + /** + * Read one line of file, reading ends when __n - 1 bytes have been read, + * or a newline (which is included in the return value), + * or an EOF (read bytes less than __n) + * Returns length of line on sucess, -1 otherwise. + * NOTE: `buf' must be end with zero. + */ + ssize_t read_line(void *__buf, size_t __n); std::shared_ptr read_content(); @@ -120,7 +129,7 @@ class File { return ::ftruncate(fd_, size) == 0; } - off_t set_offest(off_t offset) { + off_t set_offset(off_t offset) { return lseek(fd_, offset, SEEK_SET); } diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h index 07a971f72ef..26f2ab57892 100644 --- a/include/swoole_file_hook.h +++ b/include/swoole_file_hook.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,22 +19,47 @@ #include "swoole_coroutine_c_api.h" -#define access(pathname, mode) swoole_coroutine_access(pathname, mode) +#ifdef SW_USE_IOURING +#define open(pathname, flags, mode) swoole_coroutine_iouring_open(pathname, flags, mode) +#define close_file(fd) swoole_coroutine_iouring_close_file(fd) +#define read(fd, buf, count) swoole_coroutine_iouring_read(fd, buf, count) +#define write(fd, buf, count) swoole_coroutine_iouring_write(fd, buf, count) +#define rename(oldpath, newpath) swoole_coroutine_iouring_rename(oldpath, newpath) +#define mkdir(pathname, mode) swoole_coroutine_iouring_mkdir(pathname, mode) +#define unlink(pathname) swoole_coroutine_iouring_unlink(pathname) +#define rmdir(pathname) swoole_coroutine_iouring_rmdir(pathname) +#define fsync(fd) swoole_coroutine_iouring_fsync(fd) +#define fdatasync(fd) swoole_coroutine_iouring_fdatasync(fd) +#else #define open(pathname, flags, mode) swoole_coroutine_open(pathname, flags, mode) +#define close_file(fd) swoole_coroutine_close_file(fd) #define read(fd, buf, count) swoole_coroutine_read(fd, buf, count) #define write(fd, buf, count) swoole_coroutine_write(fd, buf, count) #define lseek(fd, offset, whence) swoole_coroutine_lseek(fd, offset, whence) -#define fstat(fd, statbuf) swoole_coroutine_fstat(fd, statbuf) #define readlink(fd, buf, size) swoole_coroutine_readlink(fd, buf, size) #define unlink(pathname) swoole_coroutine_unlink(pathname) #define mkdir(pathname, mode) swoole_coroutine_mkdir(pathname, mode) #define rmdir(pathname) swoole_coroutine_rmdir(pathname) #define rename(oldpath, newpath) swoole_coroutine_rename(oldpath, newpath) +#define fsync(fd) swoole_coroutine_fsync(fd) +#define fdatasync(fd) swoole_coroutine_fdatasync(fd) +#endif +#ifdef HAVE_IOURING_STATX +#define fstat(fd, statbuf) swoole_coroutine_iouring_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_iouring_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_iouring_lstat(path, statbuf) +#else +#define fstat(fd, statbuf) swoole_coroutine_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_lstat(path, statbuf) +#endif + +#define access(pathname, mode) swoole_coroutine_access(pathname, mode) #define fopen(pathname, mode) swoole_coroutine_fopen(pathname, mode) #define fdopen(fd, mode) swoole_coroutine_fdopen(fd, mode) #define freopen(pathname, mode, stream) swoole_coroutine_freopen(pathname, mode, stream) -#define fread(ptr, size, nmemb, stream) swoole_coroutine_fread(ptr, size, nmemb, stream) +#define fread(ptr, size, nmemb, stream) swoole_coroutine_fread(ptr, size, nmemb, stream) #define fwrite(ptr, size, nmemb, stream) swoole_coroutine_fwrite(ptr, size, nmemb, stream) #define fgets(s, size, stream) swoole_coroutine_fgets(s, size, stream) #define fputs(s, stream) swoole_coroutine_fputs(s, stream) diff --git a/include/swoole_hash.h b/include/swoole_hash.h index 8b01123e68c..321317e4fcc 100644 --- a/include/swoole_hash.h +++ b/include/swoole_hash.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_heap.h b/include/swoole_heap.h index 7a270efd96f..b5c08fd083f 100644 --- a/include/swoole_heap.h +++ b/include/swoole_heap.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_http.h b/include/swoole_http.h index 4d2e6a28397..94a3dbdabdd 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -10,12 +10,15 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once #include "swoole.h" +#include "swoole_protocol.h" + +#include enum swHttpVersion { SW_HTTP_VERSION_10 = 1, @@ -93,6 +96,7 @@ enum swHttpStatusCode { SW_HTTP_RANGE_NOT_SATISFIABLE = 416, SW_HTTP_MISDIRECTED_REQUEST = 421, SW_HTTP_TOO_MANY_REQUESTS = 429, + SW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, SW_HTTP_INTERNAL_SERVER_ERROR = 500, SW_HTTP_NOT_IMPLEMENTED = 501, @@ -103,17 +107,36 @@ enum swHttpStatusCode { SW_HTTP_INSUFFICIENT_STORAGE = 507 }; +struct multipart_parser; + namespace swoole { +class Server; namespace http_server { //----------------------------------------------------------------- +struct FormData { + const char *multipart_boundary_buf; + uint32_t multipart_boundary_len; + multipart_parser *multipart_parser_; + String *multipart_buffer_; + String *upload_tmpfile; + std::string upload_tmpfile_fmt_; + const char *current_header_name; + size_t current_header_name_len; + size_t upload_filesize; + size_t upload_max_filesize; +}; + struct Request { public: uint8_t method; uint8_t version; uchar excepted : 1; + uchar too_large : 1; + uchar unavailable : 1; uchar header_parsed : 1; uchar tried_to_dispatch : 1; + uchar multipart_header_parsed : 1; uchar known_length : 1; uchar keep_alive : 1; @@ -123,9 +146,12 @@ struct Request { uint32_t url_offset_; uint32_t url_length_; + uint32_t max_length_; uint32_t request_line_length_; /* without \r\n */ uint32_t header_length_; /* include request_line_length + \r\n */ - uint32_t content_length_; + uint64_t content_length_; + + FormData *form_data_; String *buffer_; @@ -134,31 +160,80 @@ struct Request { clean(); buffer_ = nullptr; } - inline void clean() { + ~Request(); + void clean() { memset(this, 0, offsetof(Request, buffer_)); } int get_protocol(); int get_header_length(); int get_chunked_body_length(); void parse_header_info(); - std::string get_date_if_modified_since(); -#ifdef SW_HTTP_100_CONTINUE + bool parse_multipart_data(String *buffer); + bool init_multipart_parser(Server *server); + void destroy_multipart_parser(); + std::string get_header(const char *name); bool has_expect_header(); -#endif }; +typedef std::function ParseCookieCallback; + int get_method(const char *method_str, size_t method_len); const char *get_method_string(int method); const char *get_status_message(int code); size_t url_decode(char *str, size_t len); char *url_encode(char const *str, size_t len); +int dispatch_request(Server *serv, const Protocol *proto, network::Socket *socket, const RecvData *rdata); +bool parse_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); +void parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb); -#ifdef SW_USE_HTTP2 -ssize_t get_package_length(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); uint8_t get_package_length_size(network::Socket *conn); -int dispatch_frame(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); -#endif +int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); + +struct ContextImpl; + +class Context { + public: + Context(Server *server, SessionId session_id, ContextImpl *_impl) { + server_ = server; + session_id_ = session_id; + impl = _impl; + } + ~Context(); + bool end(const std::string &data) { + return end(data.c_str(), data.length()); + } + bool end(const char *data, size_t length); + void setHeader(const std::string &key, const std::string &value) { + response.headers[key] = value; + } + void setStatusCode(int code) { + response.code = code; + } + // Request + int version = 0; + bool keepalive = false; + bool post_form_urlencoded = false; + std::string request_path; + std::string query_string; + std::string server_protocol; + std::unordered_map headers; + std::unordered_map files; + std::unordered_map form_data; + std::string body; + // Response + struct { + int code = 200; + std::unordered_map headers; + } response; + // Impl + Server *server_; + SessionId session_id_; + ContextImpl *impl; +}; +std::shared_ptr listen(const std::string addr, std::function cb, int mode = 1); //----------------------------------------------------------------- } // namespace http_server } // namespace swoole diff --git a/include/swoole_http2.h b/include/swoole_http2.h index cd9a5f3cdcc..9f3b6e5c395 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -10,29 +10,32 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ #pragma once +#include "swoole_protocol.h" + #define SW_HTTP2_PRI_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" enum swHttp2ErrorCode { - SW_HTTP2_ERROR_NO_ERROR = 0, - SW_HTTP2_ERROR_PROTOCOL_ERROR = 1, - SW_HTTP2_ERROR_INTERNAL_ERROR = 2, - SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 3, - SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 4, - SW_HTTP2_ERROR_STREAM_CLOSED = 5, - SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 6, - SW_HTTP2_ERROR_REFUSED_STREAM = 7, - SW_HTTP2_ERROR_CANCEL = 8, - SW_HTTP2_ERROR_COMPRESSION_ERROR = 9, - SW_HTTP2_ERROR_CONNECT_ERROR = 10, - SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 11, - SW_HTTP2_ERROR_INADEQUATE_SECURITY = 12, + SW_HTTP2_ERROR_NO_ERROR = 0x0, + SW_HTTP2_ERROR_PROTOCOL_ERROR = 0x1, + SW_HTTP2_ERROR_INTERNAL_ERROR = 0x2, + SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 0x3, + SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 0x4, + SW_HTTP2_ERROR_STREAM_CLOSED = 0x5, + SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 0x6, + SW_HTTP2_ERROR_REFUSED_STREAM = 0x7, + SW_HTTP2_ERROR_CANCEL = 0x8, + SW_HTTP2_ERROR_COMPRESSION_ERROR = 0x9, + SW_HTTP2_ERROR_CONNECT_ERROR = 0xa, + SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 0xb, + SW_HTTP2_ERROR_INADEQUATE_SECURITY = 0xc, + SW_HTTP2_ERROR_HTTP_1_1_REQUIRED = 0xd, }; enum swHttp2FrameType { @@ -76,6 +79,7 @@ enum swHttp2StreamFlag { #define SW_HTTP2_FRAME_HEADER_SIZE 9 #define SW_HTTP2_SETTING_OPTION_SIZE 6 +#define SW_HTTP2_SETTING_FRAME_SIZE (SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 6) #define SW_HTTP2_FRAME_PING_PAYLOAD_SIZE 8 #define SW_HTTP2_RST_STREAM_SIZE 4 @@ -87,24 +91,31 @@ enum swHttp2StreamFlag { #define SW_HTTP2_STREAM_ID_SIZE 4 #define SW_HTTP2_SETTINGS_PARAM_SIZE 6 -#define swoole_http2_frame_trace_log(_trace_fn, _trace_str, ...) \ +#define swoole_http2_frame_trace_log(_trace_str, ...) \ swoole_trace_log(SW_TRACE_HTTP2, \ - "%s [" SW_ECHO_GREEN "] frame" \ - " " _trace_str, \ - #_trace_fn, \ + SW_ECHO_RED_BG " [" SW_ECHO_GREEN "] " \ + " " _trace_str, \ + " RECV ", \ swoole::http2::get_type(type), \ length, \ swoole::http2::get_flag_string(flags).c_str(), \ stream_id, \ ##__VA_ARGS__) +#define swoole_http2_send_trace_log(_trace_str, ...) \ + swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_GREEN_BG " " _trace_str, " SEND ", ##__VA_ARGS__) + +#define swoole_http2_recv_trace_log(_trace_str, ...) \ + swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_RED_BG " " _trace_str, " RECV ", ##__VA_ARGS__) + namespace swoole { namespace http2 { struct Settings { uint32_t header_table_size; - uint32_t window_size; + uint32_t enable_push; uint32_t max_concurrent_streams; + uint32_t init_window_size; uint32_t max_frame_size; uint32_t max_header_list_size; }; @@ -133,17 +144,21 @@ static sw_inline ssize_t get_length(const char *buf) { return (((uint8_t) buf[0]) << 16) + (((uint8_t) buf[1]) << 8) + (uint8_t) buf[2]; } -ssize_t get_frame_length(Protocol *protocol, network::Socket *conn, const char *buf, uint32_t length); +void put_default_setting(enum swHttp2SettingId id, uint32_t value); +uint32_t get_default_setting(enum swHttp2SettingId id); +size_t pack_setting_frame(char *buf, const Settings &settings, bool server_side); +ssize_t get_frame_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); int send_setting_frame(Protocol *protocol, network::Socket *conn); const char *get_type(int type); int get_type_color(int type); static sw_inline void init_settings(Settings *settings) { - settings->header_table_size = SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE; - settings->window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - settings->max_concurrent_streams = SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS; - settings->max_frame_size = SW_HTTP2_MAX_MAX_FRAME_SIZE; - settings->max_header_list_size = SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE; + settings->header_table_size = get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE); + settings->enable_push = get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH); + settings->max_concurrent_streams = get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + settings->init_window_size = get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); + settings->max_frame_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE); + settings->max_header_list_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE); } static inline const std::string get_flag_string(int __flags) { diff --git a/include/swoole_iouring.h b/include/swoole_iouring.h new file mode 100644 index 00000000000..fbc0ed88e57 --- /dev/null +++ b/include/swoole_iouring.h @@ -0,0 +1,102 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ + +#ifndef SWOOLE_SRC_SWOOLE_IOURING_H +#define SWOOLE_SRC_SWOOLE_IOURING_H + +#include "swoole_coroutine.h" + +#ifdef SW_USE_IOURING +#include + +#ifdef HAVE_IOURING_FUTEX +#ifndef FUTEX2_SIZE_U32 +#define FUTEX2_SIZE_U32 0x02 +#endif +#endif + +using swoole::Coroutine; + +enum swIouringFlag { + SW_IOURING_DEFAULT = 0, + SW_IOURING_SQPOLL = IORING_SETUP_SQPOLL, +}; + +namespace swoole { + +struct IouringEvent; + +class Iouring { + private: + uint64_t task_num = 0; + uint64_t entries = 8192; + struct io_uring ring; + std::queue waiting_tasks; + network::Socket *ring_socket = nullptr; + Reactor *reactor = nullptr; + + Iouring(Reactor *reactor_); + bool ready(); + bool submit(IouringEvent *event); + bool dispatch(IouringEvent *event); + bool wakeup(); + + struct io_uring_sqe *get_iouring_sqe() { + struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); + // We need to reset the values of each sqe structure so that they can be used in a loop. + if (sqe) { + memset(sqe, 0, sizeof(struct io_uring_sqe)); + } + return sqe; + } + + static ssize_t execute(IouringEvent *event); + + public: + ~Iouring(); + + bool is_empty_waiting_tasks() { + return waiting_tasks.size() == 0; + } + + uint64_t get_task_num() { + return task_num; + } + + static int open(const char *pathname, int flags, int mode); + static int close(int fd); + static ssize_t read(int fd, void *buf, size_t size); + static ssize_t write(int fd, const void *buf, size_t size); + static ssize_t rename(const char *oldpath, const char *newpath); + static int mkdir(const char *pathname, mode_t mode); + static int unlink(const char *pathname); +#ifdef HAVE_IOURING_STATX + static int fstat(int fd, struct stat *statbuf); + static int stat(const char *path, struct stat *statbuf); +#endif + static int rmdir(const char *pathname); + static int fsync(int fd); + static int fdatasync(int fd); +#ifdef HAVE_IOURING_FUTEX + static int futex_wait(uint32_t *futex); + static int futex_wakeup(uint32_t *futex); +#endif + + static int callback(Reactor *reactor, Event *event); +}; +}; // namespace swoole +#endif +#endif diff --git a/include/swoole_lock.h b/include/swoole_lock.h index fae1b324c01..a88f9789348 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -29,11 +29,9 @@ class Lock { enum Type { NONE, RW_LOCK = 1, - FILE_LOCK = 2, MUTEX = 3, - SEM = 4, SPIN_LOCK = 5, - ATOMIC_LOCK = 6, + COROUTINE_LOCK = 6, }; Type get_type() { return type_; @@ -58,6 +56,7 @@ struct MutexImpl; class Mutex : public Lock { MutexImpl *impl; + int flags_; public: enum Flag { @@ -106,4 +105,41 @@ class SpinLock : public Lock { int trylock() override; }; #endif + +class CoroutineLock : public Lock { + private: + long cid = 0; + sw_atomic_t *value = nullptr; + void *coroutine = nullptr; + + int lock_impl(bool blocking = true); + + public: + CoroutineLock(bool shared); + ~CoroutineLock(); + int lock_rd() override; + int lock() override; + int unlock() override; + int trylock_rd() override; + int trylock() override; +}; + +#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) +#define SW_USE_PTHREAD_BARRIER +#endif + +struct Barrier { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_t barrier_; + pthread_barrierattr_t barrier_attr_; + bool shared_; +#else + sw_atomic_t count_; + sw_atomic_t barrier_; +#endif + void init(bool shared, int count); + void wait(); + void destroy(); +}; + } // namespace swoole diff --git a/include/swoole_log.h b/include/swoole_log.h index 62317967cbf..2eb2175804a 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -88,13 +88,13 @@ swoole::Logger *sw_logger(); #define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str()) #define swoole_info(str, ...) \ - if (SW_LOG_INFO >= sw_logger()->get_level()) { \ + if (SW_LOG_INFO >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_INFO, sw_error, _sw_error_len); \ } #define swoole_notice(str, ...) \ - if (SW_LOG_NOTICE >= sw_logger()->get_level()) { \ + if (SW_LOG_NOTICE >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len); \ } @@ -102,7 +102,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_notice(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= sw_logger()->get_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(:%d): " str ", Error: %s[%d]", \ @@ -117,7 +117,7 @@ swoole::Logger *sw_logger(); #define swoole_warning(str, ...) \ do { \ - if (SW_LOG_WARNING >= sw_logger()->get_level()) { \ + if (SW_LOG_WARNING >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(): " str, __SW_FUNC__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len); \ } \ @@ -126,7 +126,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_warning(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= sw_logger()->get_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(): " str ", Error: %s[%d]", \ @@ -142,7 +142,7 @@ swoole::Logger *sw_logger(); do { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ + swoole_exit(1); \ } while (0) #define swoole_sys_error(str, ...) \ @@ -155,19 +155,15 @@ swoole::Logger *sw_logger(); swoole_strerror(errno), \ errno); \ sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ + swoole_exit(1); \ } while (0) -#define swoole_fatal_error(code, str, ...) \ - do { \ - SwooleG.fatal_error(code, str, ##__VA_ARGS__); \ - exit(255); \ - } while (0) +#define swoole_fatal_error(code, str, ...) SwooleG.fatal_error(code, str, ##__VA_ARGS__) #define swoole_error_log(level, error, str, ...) \ do { \ swoole_set_last_error(error); \ - if (level >= sw_logger()->get_level()) { \ + if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s() (ERRNO %d): " str, __SW_FUNC__, error, ##__VA_ARGS__); \ sw_logger()->put(level, sw_error, _sw_error_len); \ @@ -176,7 +172,7 @@ swoole::Logger *sw_logger(); #ifdef SW_DEBUG #define swoole_debug(str, ...) \ - if (SW_LOG_DEBUG >= sw_logger()->get_level()) { \ + if (SW_LOG_DEBUG >= swoole_get_log_level()) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_DEBUG, sw_error, _sw_error_len); \ @@ -195,7 +191,7 @@ swoole::Logger *sw_logger(); hof += sprintf(hex + hof, "%02x ", (__data)[i] & 0xff); \ sof += sprintf(str + sof, "%c", isprint((int) (__data)[i]) ? (__data)[i] : '.'); \ } \ - swoole_debug("| %08x | %-48s| %-16s |", of, hex, str); \ + swoole_debug("| %08zx | %-48s| %-16s |", of, hex, str); \ } \ swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ } while (0) @@ -204,7 +200,7 @@ swoole::Logger *sw_logger(); #define swoole_hex_dump(data, length) #endif -enum swTrace_type { +enum swTraceWhat : long { /** * Server */ @@ -245,12 +241,18 @@ enum swTrace_type { SW_TRACE_CO_CURL = 1u << 29, SW_TRACE_CARES = 1u << 30, + SW_TRACE_ZLIB = 1u << 31, + SW_TRACE_CO_PGSQL = 1ul << 32, + SW_TRACE_CO_ODBC = 1ul << 33, + SW_TRACE_CO_ORACLE = 1ul << 34, + SW_TRACE_CO_SQLITE = 1ul << 35, + SW_TRACE_ALL = 0x7fffffffffffffff }; #ifdef SW_LOG_TRACE_OPEN #define swoole_trace_log(what, str, ...) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (what & SwooleG.trace_flags)) { \ + if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_TRACE, sw_error, _sw_error_len); \ diff --git a/include/swoole_lru_cache.h b/include/swoole_lru_cache.h index 73a88fbf0fd..000421507bd 100644 --- a/include/swoole_lru_cache.h +++ b/include/swoole_lru_cache.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -40,7 +40,7 @@ class LRUCache { cache_capacity = capacity; } - inline std::shared_ptr get(const std::string &key) { + std::shared_ptr get(const std::string &key) { auto iter = cache_map.find(key); if (iter == cache_map.end()) { return nullptr; @@ -54,7 +54,7 @@ class LRUCache { return iter->second->second.second; // iter -> list::iter -> cache_node_t -> value } - inline void set(const std::string &key, const std::shared_ptr &val, time_t expire = 0) { + void set(const std::string &key, const std::shared_ptr &val, time_t expire = 0) { time_t expire_time; if (expire <= 0) { @@ -82,7 +82,7 @@ class LRUCache { cache_map[key] = cache_list.begin(); } - inline void del(const std::string &key) { + void del(const std::string &key) { auto iter = cache_map.find(key); if (iter == cache_map.end()) { return; @@ -92,7 +92,7 @@ class LRUCache { cache_map.erase(iter); } - inline void clear() { + void clear() { cache_list.clear(); cache_map.clear(); } diff --git a/include/swoole_memory.h b/include/swoole_memory.h index 0f9f30aafe4..022fda3625b 100644 --- a/include/swoole_memory.h +++ b/include/swoole_memory.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -27,8 +27,9 @@ class MemoryPool { virtual ~MemoryPool(){}; virtual void *alloc(uint32_t size) = 0; virtual void free(void *ptr) = 0; + protected: - MemoryPool() {}; + MemoryPool(){}; }; struct FixedPoolImpl; @@ -43,7 +44,7 @@ class FixedPool : public MemoryPool { ~FixedPool(); void *alloc(uint32_t size); void free(void *ptr); - void debug(); + void debug(int max_lines = 100); uint32_t get_number_of_spare_slice(); uint32_t get_number_of_total_slice(); uint32_t get_slice_size(); diff --git a/include/swoole_message_bus.h b/include/swoole_message_bus.h new file mode 100644 index 00000000000..faae0d1d69d --- /dev/null +++ b/include/swoole_message_bus.h @@ -0,0 +1,203 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" + +#include + +namespace swoole { + +struct PipeBuffer { + DataHead info; + char data[0]; + + bool is_begin() { + return info.flags & SW_EVENT_DATA_BEGIN; + } + + bool is_chunked() { + return info.flags & SW_EVENT_DATA_CHUNK; + } + + bool is_end() { + return info.flags & SW_EVENT_DATA_END; + } +}; + +struct PacketPtr { + size_t length; + char *data; +}; + +struct DgramPacket { + SocketType socket_type; + network::Address socket_addr; + uint32_t length; + char data[0]; +}; + +struct PacketTask { + size_t length; + char tmpfile[SW_TASK_TMP_PATH_SIZE]; +}; + +class MessageBus { + private: + const Allocator *allocator_; + std::unordered_map> packet_pool_; + std::vector pipe_sockets_; + std::function id_generator_; + size_t buffer_size_; + PipeBuffer *buffer_ = nullptr; + bool always_chunked_transfer_ = false; + + String *get_packet_buffer(); + ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer); + + public: + MessageBus() { + allocator_ = sw_std_allocator(); + buffer_size_ = SW_BUFFER_SIZE_STD; + } + + ~MessageBus(); + + bool empty() { + return packet_pool_.empty(); + } + + size_t count() { + return packet_pool_.size(); + } + + void clear() { + packet_pool_.clear(); + } + + void set_allocator(const Allocator *allocator) { + allocator_ = allocator; + } + + void set_id_generator(const std::function &id_generator) { + id_generator_ = id_generator; + } + + void set_buffer_size(size_t buffer_size) { + buffer_size_ = buffer_size; + } + + void set_always_chunked_transfer() { + always_chunked_transfer_ = true; + } + + size_t get_buffer_size() { + return buffer_size_; + } + + size_t get_memory_size(); + + bool alloc_buffer() { + void *_ptr = allocator_->malloc(sizeof(*buffer_) + buffer_size_); + if (_ptr) { + buffer_ = (PipeBuffer *) _ptr; + sw_memset_zero(&buffer_->info, sizeof(buffer_->info)); + return true; + } else { + return false; + } + } + + /** + * If use the zend_string_allocator, must manually call this function to release the memory, + * otherwise coredump will occur when php shutdown, because zend_string has been released + */ + void free_buffer() { + allocator_->free(buffer_); + buffer_ = nullptr; + } + + void pass(SendData *task) { + memcpy(&buffer_->info, &task->info, sizeof(buffer_->info)); + if (task->info.len > 0) { + buffer_->info.flags = SW_EVENT_DATA_PTR; + PacketPtr pkt{task->info.len, (char *) task->data}; + buffer_->info.len = sizeof(pkt); + memcpy(buffer_->data, &pkt, sizeof(pkt)); + } + } + + /** + * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. + * Otherwise send it directly. + * When sending data in multi-thread environment, must use get_pipe_socket() to separate socket memory. + * @return: send success returns true, send failure returns false. + */ + bool write(network::Socket *sock, SendData *packet); + /** + * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool. + * Then continue to listen to readable events, waiting for more chunks. + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read(network::Socket *sock); + /** + * Receive data from pipeline, and store data to buffer + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read_with_buffer(network::Socket *sock); + /** + * The last chunk of data has been received, return address and length, start processing this packet. + */ + PacketPtr get_packet() const; + PipeBuffer *get_buffer() { + return buffer_; + } + /** + * Pop the data memory address to the outer layer, no longer managed by MessageBus + */ + char *move_packet() { + uint64_t msg_id = buffer_->info.msg_id; + auto iter = packet_pool_.find(msg_id); + if (iter != packet_pool_.end()) { + auto str = iter->second.get(); + char *val = str->str; + str->str = nullptr; + return val; + } else { + return nullptr; + } + } + /** + * The processing of this data packet has been completed, and the relevant memory has been released + */ + void pop() { + if (buffer_->is_end()) { + packet_pool_.erase(buffer_->info.msg_id); + } + } + /** + * It is possible to operate the same pipe in multiple threads. + * Each thread must have a unique buffer and the socket memory must be separated. + */ + network::Socket *get_pipe_socket(network::Socket *sock) { + return pipe_sockets_[sock->get_fd()]; + } + void init_pipe_socket(network::Socket *sock); +}; +} // namespace swoole diff --git a/include/swoole_mime_type.h b/include/swoole_mime_type.h index 10cdbf9a92e..27a7122c627 100644 --- a/include/swoole_mime_type.h +++ b/include/swoole_mime_type.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_mqtt.h b/include/swoole_mqtt.h index 4fa77192112..1c0ee7f10c2 100644 --- a/include/swoole_mqtt.h +++ b/include/swoole_mqtt.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once @@ -57,7 +57,7 @@ struct Packet { char protocol_name[8]; }; -ssize_t get_package_length(swProtocol *protocol, swSocket *conn, const char *data, uint32_t size); -void set_protocol(swProtocol *protocol); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); +void set_protocol(Protocol *protocol); } // namespace mqtt } // namespace swoole diff --git a/include/swoole_msg_queue.h b/include/swoole_msg_queue.h index 4a5d5a3481c..da8c021ba6f 100644 --- a/include/swoole_msg_queue.h +++ b/include/swoole_msg_queue.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -23,9 +23,14 @@ namespace swoole { +enum { + SW_MSGQUEUE_ORIENT = 1, + SW_MSGQUEUE_BALANCE = 2, +}; + struct QueueNode { - long mtype; /* type of received/sent message */ - char mdata[sizeof(EventData)]; /* text of the message */ + long mtype; /* type of received/sent message */ + char mdata[sizeof(EventData)]; /* text of the message */ }; class MsgQueue { @@ -35,6 +40,7 @@ class MsgQueue { key_t msg_key_; int flags_; int perms_; + public: explicit MsgQueue(key_t msg_key, bool blocking = true, int perms = 0); ~MsgQueue(); @@ -54,4 +60,4 @@ class MsgQueue { bool stat(size_t *queue_num, size_t *queue_bytes); bool destroy(); }; -} +} // namespace swoole diff --git a/include/swoole_pipe.h b/include/swoole_pipe.h index cd24299b0a2..df7ced681c6 100644 --- a/include/swoole_pipe.h +++ b/include/swoole_pipe.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 19c762c0455..9418a19d644 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -26,6 +26,7 @@ #include "swoole_pipe.h" #include "swoole_channel.h" #include "swoole_msg_queue.h" +#include "swoole_message_bus.h" enum swWorkerStatus { SW_WORKER_BUSY = 1, @@ -46,6 +47,12 @@ enum WorkerMessageType { SW_WORKER_MESSAGE_STOP = 1, }; +enum ProtocolType { + SW_PROTOCOL_TASK = 1, + SW_PROTOCOL_STREAM, + SW_PROTOCOL_MESSAGE, +}; + struct WorkerStopMessage { pid_t pid; uint16_t worker_id; @@ -96,27 +103,30 @@ struct ProcessPool; struct Worker; struct WorkerGlobal { - bool run_always; bool shutdown; + bool running; uint32_t max_request; + /** + * worker is shared memory, visible in other work processes. + * When a worker process restarts, it may be held by both the old and new processes simultaneously, + * necessitating careful handling of the state. + */ Worker *worker; + /** + * worker_copy is a copy of worker, + * but it must be local memory and only used within the current process or thread. + * It is not visible to other worker processes. + */ + Worker *worker_copy; time_t exit_time; - uint32_t worker_concurrency = 0; }; struct Worker { - /** - * worker process - */ pid_t pid; - - /** - * worker thread - */ - pthread_t tid; - + WorkerId id; ProcessPool *pool; MsgQueue *queue; + bool shared; bool redirect_stdout; bool redirect_stdin; @@ -127,14 +137,10 @@ struct Worker { */ uint8_t status; uint8_t type; - uint8_t ipc_mode; + uint8_t msgqueue_mode; uint8_t child_process; - /** - * tasking num - */ - sw_atomic_t tasking_num; - + uint32_t concurrency; time_t start_time; sw_atomic_long_t dispatch_count; @@ -142,13 +148,7 @@ struct Worker { sw_atomic_long_t response_count; size_t coroutine_num; - /** - * worker id - */ - WorkerId id; - Mutex *lock; - UnixSocket *pipe_object; network::Socket *pipe_master; @@ -159,11 +159,34 @@ struct Worker { void *ptr2; ssize_t send_pipe_message(const void *buf, size_t n, int flags); + bool has_exceeded_max_request(); + void set_max_request(uint32_t max_request, uint32_t max_request_grace); + void report_error(const ExitStatus &exit_status); + /** + * Init global state for worker process. + * Must be called after the process is spawned and before the main loop is executed. + */ + void init(); + void shutdown(); + bool is_shutdown(); + bool is_running(); void set_status(enum swWorkerStatus _status) { status = _status; } + void set_status_to_idle() { + set_status(SW_WORKER_IDLE); + } + + void set_status_to_busy() { + set_status(SW_WORKER_BUSY); + } + + void add_request_count() { + request_count++; + } + bool is_busy() { return status == SW_WORKER_BUSY; } @@ -191,7 +214,10 @@ struct ProcessPool { bool read_message; bool started; bool schedule_by_sysvmsg; + bool async; + uint8_t ipc_mode; + enum ProtocolType protocol_type_; pid_t master_pid; uint32_t reload_worker_i; uint32_t max_wait_time; @@ -237,10 +263,14 @@ struct ProcessPool { uint8_t scheduler_warning; time_t warning_time; - int (*onTask)(ProcessPool *pool, EventData *task); - void (*onWorkerStart)(ProcessPool *pool, int worker_id); - void (*onMessage)(ProcessPool *pool, const char *data, uint32_t length); - void (*onWorkerStop)(ProcessPool *pool, int worker_id); + void (*onStart)(ProcessPool *pool); + void (*onShutdown)(ProcessPool *pool); + int (*onTask)(ProcessPool *pool, Worker *worker, EventData *task); + void (*onWorkerStart)(ProcessPool *pool, Worker *worker); + void (*onMessage)(ProcessPool *pool, RecvData *msg); + void (*onWorkerExit)(ProcessPool *pool, Worker *worker); + void (*onWorkerStop)(ProcessPool *pool, Worker *worker); + void (*onWorkerError)(ProcessPool *pool, Worker *worker, const ExitStatus &exit_status); void (*onWorkerMessage)(ProcessPool *pool, EventData *msg); int (*onWorkerNotFound)(ProcessPool *pool, const ExitStatus &exit_status); int (*main_loop)(ProcessPool *pool, Worker *worker); @@ -250,14 +280,14 @@ struct ProcessPool { Worker *workers; std::vector> *pipes; std::unordered_map *map_; - Reactor *reactor; MsgQueue *queue; StreamInfo *stream_info_; Channel *message_box = nullptr; + MessageBus *message_bus = nullptr; void *ptr; - inline void set_type(int _type) { + void set_type(int _type) { uint32_t i; type = _type; for (i = 0; i < worker_num; i++) { @@ -265,7 +295,7 @@ struct ProcessPool { } } - inline void set_start_id(int _start_id) { + void set_start_id(int _start_id) { uint32_t i; start_id = _start_id; for (i = 0; i < worker_num; i++) { @@ -273,7 +303,7 @@ struct ProcessPool { } } - inline Worker *get_worker(int worker_id) { + Worker *get_worker(int worker_id) { return &(workers[worker_id - start_id]); } @@ -285,31 +315,64 @@ struct ProcessPool { return iter->second; } + TaskId get_task_id(EventData *task) { + return task->info.fd; + } + + WorkerId get_task_src_worker_id(EventData *task) { + return task->info.reactor_id; + } + + void set_max_packet_size(uint32_t _max_packet_size) { + max_packet_size_ = _max_packet_size; + } + + bool is_master() { + return swoole_get_process_type() == SW_PROCESS_MASTER; + } + + bool is_worker() { + return swoole_get_process_type() == SW_PROCESS_WORKER; + } + + void set_protocol(enum ProtocolType _protocol_type); + void set_max_request(uint32_t _max_request, uint32_t _max_request_grace); int get_max_request(); - void set_protocol(int task_protocol, uint32_t max_packet_size); bool detach(); int wait(); + int start_check(); int start(); void shutdown(); bool reload(); pid_t spawn(Worker *worker); - int dispatch(EventData *data, int *worker_id); + void stop(Worker *worker); + void kill_all_workers(int signo = SIGKILL); + swResultCode dispatch(EventData *data, int *worker_id); int response(const char *data, int length); - int dispatch_blocking(EventData *data, int *dst_worker_id); - int dispatch_blocking(const char *data, uint32_t len); - int add_worker(Worker *worker); + swResultCode dispatch_blocking(EventData *data, int *dst_worker_id); + swResultCode dispatch_blocking(const char *data, uint32_t len); + void add_worker(Worker *worker); int del_worker(Worker *worker); void destroy(); int create(uint32_t worker_num, key_t msgqueue_key = 0, swIPCMode ipc_mode = SW_IPC_NONE); int create_message_box(size_t memory_size); + int create_message_bus(); int push_message(uint8_t type, const void *data, size_t length); int push_message(EventData *msg); int pop_message(void *data, size_t size); int listen(const char *socket_file, int blacklog); int listen(const char *host, int port, int blacklog); int schedule(); + bool is_worker_running(Worker *worker); + static void kill_timeout_worker(Timer *timer, TimerNode *tnode); + + private: + static int run_with_task_protocol(ProcessPool *pool, Worker *worker); + static int run_with_stream_protocol(ProcessPool *pool, Worker *worker); + static int run_with_message_protocol(ProcessPool *pool, Worker *worker); + static int run_async(ProcessPool *pool, Worker *worker); }; }; // namespace swoole @@ -325,4 +388,10 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { return kill(__pid, __sig); } -extern swoole::WorkerGlobal SwooleWG; // Worker Global Variable +typedef swoole::ProtocolType swProtocolType; + +extern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG; + +static inline swoole::Worker *sw_worker() { + return SwooleWG.worker; +} diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index d296d8acebf..179b4db9c32 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -25,8 +25,15 @@ #include namespace swoole { + +struct PacketLength { + const char *buf; + uint32_t buf_size; + uint32_t header_len; +}; + struct Protocol { - typedef ssize_t (*LengthFunc)(Protocol *, network::Socket *, const char *, uint32_t); + typedef ssize_t (*LengthFunc)(const Protocol *, network::Socket *, PacketLength *pl); /* one package: eof check */ bool split_by_eof; @@ -39,12 +46,19 @@ struct Protocol { uint16_t package_body_offset; uint32_t package_max_length; - void *private_data; + void *private_data_1; void *private_data_2; - uint16_t real_header_length; - uint16_t ext_flags; - int (*onPackage)(Protocol *, network::Socket *, const char *, uint32_t); + /** + * callback this function when a complete data packet is received + */ + int (*onPackage)(const Protocol *, network::Socket *, const RecvData *); + /** + * parse the length value in the received data + * @return 0: more data needs to be received + * @return -1: abnormal value, connection should be closed + * @return >0: the length of the data packet + */ LengthFunc get_package_length; uint8_t (*get_package_length_size)(network::Socket *); @@ -52,11 +66,7 @@ struct Protocol { int recv_with_length_protocol(network::Socket *socket, String *buffer); int recv_split_by_eof(network::Socket *socket, String *buffer); - static ssize_t default_length_func(Protocol *protocol, network::Socket *socket, const char *data, uint32_t size); - - inline static LengthFunc get_function(const std::string &name) { - return (LengthFunc) swoole_get_function(name.c_str(), name.length()); - } + static ssize_t default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl); }; } // namespace swoole @@ -154,11 +164,9 @@ static sw_inline uint64_t swoole_ntoh64(uint64_t net) { return ret; } -#ifdef SW_DEBUG void swoole_dump_ascii(const char *data, size_t size); void swoole_dump_bin(const char *data, char type, size_t size); void swoole_dump_hex(const char *data, size_t outlen); -#endif char *swoole_dec2hex(ulong_t value, int base); ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes); diff --git a/include/swoole_proxy.h b/include/swoole_proxy.h index 5ff58a0e9f2..987624228a7 100644 --- a/include/swoole_proxy.h +++ b/include/swoole_proxy.h @@ -10,15 +10,24 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once #include +#include #define SW_SOCKS5_VERSION_CODE 0x05 +#define SW_HTTP_PROXY_CHECK_MESSAGE 0 +#define SW_HTTP_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established\r\n" + +#define SW_HTTP_PROXY_FMT \ + "CONNECT %.*s:%d HTTP/1.1\r\n" \ + "Host: %.*s:%d\r\n" \ + "User-Agent: Swoole/" SWOOLE_VERSION "\r\n" \ + "Proxy-Connection: Keep-Alive\r\n" enum swHttpProxyState { SW_HTTP_PROXY_STATE_WAIT = 0, @@ -39,6 +48,8 @@ enum swSocks5Method { }; namespace swoole { +class String; + struct HttpProxy { uint8_t state; uint8_t dont_handshake; @@ -48,9 +59,10 @@ struct HttpProxy { std::string password; std::string target_host; int target_port; - char buf[512]; std::string get_auth_str(); + size_t pack(String *send_buffer, const std::string *host_name); + bool handshake(String *recv_buffer); }; struct Socks5Proxy { diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index 17340a2a2ac..ff67809afd1 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -104,6 +104,8 @@ class Reactor { EXIT_CONDITION_AIO_TASK, EXIT_CONDITION_SIGNALFD, EXIT_CONDITION_USER_BEFORE_DEFAULT, + EXIT_CONDITION_FORCED_TERMINATION, + EXIT_CONDITION_IOURING, EXIT_CONDITION_DEFAULT = 999, EXIT_CONDITION_USER_AFTER_DEFAULT, }; @@ -125,10 +127,7 @@ class Reactor { bool wait_exit = false; bool destroyed = false; bool bailout = false; - /** - * callback signal - */ - bool check_signalfd = false; + /** * reactor->wait timeout (millisecond) or -1 */ @@ -199,33 +198,33 @@ class Reactor { return running; } - inline size_t remove_exit_condition(enum ExitCondition id) { + size_t remove_exit_condition(enum ExitCondition id) { return exit_conditions.erase(id); } - inline bool isset_exit_condition(enum ExitCondition id) { + bool isset_exit_condition(enum ExitCondition id) { return exit_conditions.find(id) != exit_conditions.end(); } - inline bool isset_handler(int fdtype) { + bool isset_handler(int fdtype) { return read_handler[fdtype] != nullptr; } - inline int add_event(network::Socket *_socket, EventType event_type) { + int add_event(network::Socket *_socket, EventType event_type) { if (!(_socket->events & event_type)) { return set(_socket, _socket->events | event_type); } return SW_OK; } - inline int del_event(network::Socket *_socket, EventType event_type) { + int del_event(network::Socket *_socket, EventType event_type) { if (_socket->events & event_type) { return set(_socket, _socket->events & (~event_type)); } return SW_OK; } - inline int remove_read_event(network::Socket *_socket) { + int remove_read_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_WRITE) { _socket->events &= (~SW_EVENT_READ); return set(_socket, _socket->events); @@ -234,7 +233,7 @@ class Reactor { } } - inline int remove_write_event(network::Socket *_socket) { + int remove_write_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_READ) { _socket->events &= (~SW_EVENT_WRITE); return set(_socket, _socket->events); @@ -243,7 +242,7 @@ class Reactor { } } - inline int add_read_event(network::Socket *_socket) { + int add_read_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_WRITE) { _socket->events |= SW_EVENT_READ; return set(_socket, _socket->events); @@ -252,7 +251,7 @@ class Reactor { } } - inline int add_write_event(network::Socket *_socket) { + int add_write_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_READ) { _socket->events |= SW_EVENT_WRITE; return set(_socket, _socket->events); @@ -261,11 +260,11 @@ class Reactor { } } - inline bool exists(network::Socket *_socket) { + bool exists(network::Socket *_socket) { return !_socket->removed && _socket->events; } - inline int get_timeout_msec() { + int get_timeout_msec() { return defer_tasks == nullptr ? timeout_msec : 0; } @@ -282,12 +281,12 @@ class Reactor { } void foreach_socket(const std::function &callback) { - for (auto kv : sockets_) { + for (auto &kv : sockets_) { callback(kv.first, kv.second); } } - inline ReactorHandler get_handler(EventType event_type, FdType fd_type) { + ReactorHandler get_handler(EventType event_type, FdType fd_type) { switch (event_type) { case SW_EVENT_READ: return read_handler[fd_type]; @@ -302,7 +301,7 @@ class Reactor { return nullptr; } - inline ReactorHandler get_error_handler(FdType fd_type) { + ReactorHandler get_error_handler(FdType fd_type) { ReactorHandler handler = get_handler(SW_EVENT_ERROR, fd_type); // error callback is not set, try to use readable or writable callback if (handler == nullptr) { @@ -314,29 +313,29 @@ class Reactor { return handler; } - inline void before_wait() { + void before_wait() { start = running = true; } - inline int trigger_close_event(Event *event) { + int trigger_close_event(Event *event) { return default_error_handler(this, event); } - inline void set_wait_exit(bool enable) { + void set_wait_exit(bool enable) { wait_exit = enable; } - inline void _add(network::Socket *_socket, int events) { + void _add(network::Socket *_socket, int events) { _socket->events = events; _socket->removed = 0; sockets_[_socket->fd] = _socket; } - inline void _set(network::Socket *_socket, int events) { + void _set(network::Socket *_socket, int events) { _socket->events = events; } - inline void _del(network::Socket *_socket) { + void _del(network::Socket *_socket) { _socket->events = 0; _socket->removed = 1; sockets_.erase(_socket->fd); diff --git a/include/swoole_redis.h b/include/swoole_redis.h index 421fb11c4c2..f7718adb6e9 100644 --- a/include/swoole_redis.h +++ b/include/swoole_redis.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_server.h b/include/swoole_server.h index a6efb9b50f6..d8393edcfa8 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,6 +26,10 @@ #include "swoole_process_pool.h" #include "swoole_pipe.h" #include "swoole_channel.h" +#include "swoole_message_bus.h" +#ifdef SW_THREAD +#include "swoole_lock.h" +#endif #ifdef SW_USE_OPENSSL #include "swoole_dtls.h" @@ -42,6 +46,7 @@ #include #include #include +#include //------------------------------------Server------------------------------------------- namespace swoole { @@ -53,6 +58,8 @@ struct Request; class Server; struct Manager; +typedef std::function WorkerFn; + struct Session { SessionId id; int fd; @@ -79,9 +86,7 @@ struct Connection { uint8_t overflow; uint8_t high_watermark; uint8_t http_upgrade; -#ifdef SW_USE_HTTP2 uint8_t http2_stream; -#endif #ifdef SW_HAVE_ZLIB uint8_t websocket_compression; #endif @@ -137,194 +142,29 @@ struct Connection { #ifdef SW_USE_OPENSSL String *ssl_client_cert; - uint16_t ssl_client_cert_pid; + pid_t ssl_client_cert_pid; #endif sw_atomic_t lock; }; -struct SendData { - DataHead info; - const char *data; -}; - -struct RecvData { - DataHead info; - const char *data; -}; - -struct PipeBuffer { - DataHead info; - char data[0]; - - bool is_begin() { - return info.flags & SW_EVENT_DATA_BEGIN; - } - - bool is_chunked() { - return info.flags & SW_EVENT_DATA_CHUNK; - } - - bool is_end() { - return info.flags & SW_EVENT_DATA_END; - } -}; - -//------------------------------------Packet------------------------------------------- -struct PacketPtr { - size_t length; - char *data; -}; - -struct DgramPacket { - SocketType socket_type; - network::Address socket_addr; - uint32_t length; - char data[0]; -}; - -struct PacketTask { - size_t length; - char tmpfile[SW_TASK_TMP_PATH_SIZE]; -}; - -class MessageBus { - private: - const Allocator *allocator_; - std::unordered_map> packet_pool_; - std::function id_generator_; - size_t buffer_size_; - PipeBuffer *buffer_ = nullptr; - bool always_chunked_transfer_ = false; - - String *get_packet_buffer(); - ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer); - - public: - MessageBus() { - allocator_ = sw_std_allocator(); - buffer_size_ = SW_BUFFER_SIZE_STD; - } - - ~MessageBus() { - allocator_->free(buffer_); - } - - bool empty() { - return packet_pool_.empty(); - } - - void clear() { - packet_pool_.clear(); - } - - void set_allocator(const Allocator *allocator) { - allocator_ = allocator; - } - - void set_id_generator(const std::function &id_generator) { - id_generator_ = id_generator; - } - - void set_buffer_size(size_t buffer_size) { - buffer_size_ = buffer_size; - } - - void set_always_chunked_transfer() { - always_chunked_transfer_ = true; - } - - size_t get_buffer_size() { - return buffer_size_; - } - - size_t get_memory_size(); - - bool alloc_buffer() { - void *_ptr = allocator_->malloc(sizeof(*buffer_) + buffer_size_); - if (_ptr) { - buffer_ = (PipeBuffer *) _ptr; - sw_memset_zero(&buffer_->info, sizeof(buffer_->info)); - return true; - } else { - return false; - } - } - - void pass(SendData *task) { - memcpy(&buffer_->info, &task->info, sizeof(buffer_->info)); - if (task->info.len > 0) { - buffer_->info.flags = SW_EVENT_DATA_PTR; - PacketPtr pkt{task->info.len, (char *) task->data}; - buffer_->info.len = sizeof(pkt); - memcpy(buffer_->data, &pkt, sizeof(pkt)); - } - } - - /** - * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. - * Otherwise send it directly. - * @return: send success returns true, send failure returns false. - */ - bool write(network::Socket *sock, SendData *packet); - /** - * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool. - * Then continue to listen to readable events, waiting for more chunks. - * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred - */ - ssize_t read(network::Socket *sock); - /** - * Receive data from pipeline, and store data to buffer - * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred - */ - ssize_t read_with_buffer(network::Socket *sock); - /** - * The last chunk of data has been received, return address and length, start processing this packet. - */ - PacketPtr get_packet() const; - PipeBuffer *get_buffer() { - return buffer_; - } - /** - * Pop the data memory address to the outer layer, no longer managed by MessageBus - */ - char *move_packet() { - uint64_t msg_id = buffer_->info.msg_id; - auto iter = packet_pool_.find(msg_id); - if (iter != packet_pool_.end()) { - auto str = iter->second.get(); - char *val = str->str; - str->str = nullptr; - return val; - } else { - return nullptr; - } - } - /** - * The processing of this data packet has been completed, and the relevant memory has been released - */ - void pop() { - if (buffer_->is_end()) { - packet_pool_.erase(buffer_->info.msg_id); - } - } -}; - //------------------------------------ReactorThread------------------------------------------- struct ReactorThread { int id; std::thread thread; network::Socket *notify_pipe = nullptr; - uint32_t pipe_num = 0; uint64_t dispatch_count = 0; - network::Socket *pipe_sockets = nullptr; network::Socket *pipe_command = nullptr; MessageBus message_bus; int init(Server *serv, Reactor *reactor, uint16_t reactor_id); + void shutdown(Reactor *reactor); + int close_connection(Reactor *reactor, SessionId session_id); + void clean(); }; struct ServerPortGS { sw_atomic_t connection_num; + sw_atomic_t *connection_nums = nullptr; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; sw_atomic_long_t close_count; @@ -358,7 +198,7 @@ struct ListenPort { int tcp_user_timeout = 0; - uint16_t max_idle_time = 0; + double max_idle_time = 0; int socket_buffer_size = network::Socket::default_buffer_size; uint32_t buffer_high_watermark = 0; @@ -453,35 +293,41 @@ struct ListenPort { int (*onRead)(Reactor *reactor, ListenPort *port, Event *event) = nullptr; - inline bool is_dgram() { + bool is_dgram() { return network::Socket::is_dgram(type); } - inline bool is_stream() { + bool is_stream() { return network::Socket::is_stream(type); } - inline void set_eof_protocol(const std::string &eof, bool find_from_right = false) { + void set_eof_protocol(const std::string &eof, bool find_from_right = false) { open_eof_check = true; protocol.split_by_eof = !find_from_right; protocol.package_eof_len = std::min(eof.length(), sizeof(protocol.package_eof)); memcpy(protocol.package_eof, eof.c_str(), protocol.package_eof_len); } - inline void set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset) { + void set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset) { open_length_check = true; protocol.package_length_type = length_type; protocol.package_length_size = swoole_type_size(length_type); - protocol.package_body_offset = length_offset; + protocol.package_length_offset = length_offset; protocol.package_body_offset = body_offset; } + void set_package_max_length(uint32_t max_length) { + protocol.package_max_length = max_length; + } + ListenPort(); ~ListenPort() = default; int listen(); void close(); bool import(int sock); const char *get_protocols(); + int create_socket(Server *server); + void close_socket(); #ifdef SW_USE_OPENSSL bool ssl_create_context(SSLContext *context); @@ -497,21 +343,23 @@ struct ListenPort { } #endif void clear_protocol(); - inline network::Socket *get_socket() { + network::Socket *get_socket() { return socket; } - int get_port() { + int get_port() const { return port; } - const char *get_host() { + const char *get_host() const { return host.c_str(); } - SocketType get_type() { + SocketType get_type() const { return type; } - int get_fd() { + int get_fd() const { return socket ? socket->fd : -1; } + + size_t get_connection_num() const; }; struct ServerGS { @@ -525,9 +373,13 @@ struct ServerGS { int max_fd; int min_fd; + bool called_onStart; time_t start_time; sw_atomic_t connection_num; + sw_atomic_t *connection_nums = nullptr; sw_atomic_t tasking_num; + uint32_t max_concurrency; + sw_atomic_t concurrency; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; sw_atomic_long_t close_count; @@ -537,13 +389,11 @@ struct ServerGS { sw_atomic_long_t total_recv_bytes; sw_atomic_long_t total_send_bytes; sw_atomic_long_t pipe_packet_msg_id; + sw_atomic_long_t task_count; sw_atomic_t spinlock; -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_t manager_barrier; - pthread_barrierattr_t manager_barrier_attr; -#endif + Barrier manager_barrier; ProcessPool task_workers; ProcessPool event_workers; @@ -557,6 +407,13 @@ class Factory { Factory(Server *_server) { server_ = _server; } + pid_t spawn_event_worker(Worker *worker); + pid_t spawn_user_worker(Worker *worker); + pid_t spawn_task_worker(Worker *worker); + void kill_user_workers(); + void kill_event_workers(); + void kill_task_workers(); + void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); virtual ~Factory() {} virtual bool start() = 0; virtual bool shutdown() = 0; @@ -568,7 +425,7 @@ class Factory { class BaseFactory : public Factory { public: - BaseFactory(Server *server) : Factory(server) {} + BaseFactory(Server *server); ~BaseFactory(); bool start() override; bool shutdown() override; @@ -576,12 +433,10 @@ class BaseFactory : public Factory { bool finish(SendData *) override; bool notify(DataHead *) override; bool end(SessionId sesion_id, int flags) override; + bool forward_message(Session *session, SendData *data); }; class ProcessFactory : public Factory { - private: - std::vector> pipes; - public: ProcessFactory(Server *server); ~ProcessFactory(); @@ -593,6 +448,36 @@ class ProcessFactory : public Factory { bool end(SessionId sesion_id, int flags) override; }; +class ThreadFactory : public BaseFactory { + private: + std::vector threads_; + std::mutex lock_; + std::condition_variable cv_; + std::queue queue_; + long cv_timeout_ms_; + bool reload_all_workers; + bool reloading; + Worker manager; + template + void create_thread(int i, _Callable fn); + void join_thread(std::thread &thread); + void at_thread_exit(Worker *worker); + void create_message_bus(); + void destroy_message_bus(); + + public: + ThreadFactory(Server *server); + ~ThreadFactory(); + void spawn_event_worker(WorkerId i); + void spawn_task_worker(WorkerId i); + void spawn_user_worker(WorkerId i); + void spawn_manager_thread(WorkerId i); + void wait(); + bool reload(bool reload_all_workers); + bool start() override; + bool shutdown() override; +}; + enum ServerEventType { // recv data payload SW_SERVER_EVENT_RECV_DATA, @@ -645,6 +530,7 @@ class Server { enum Mode { MODE_BASE = 1, MODE_PROCESS = 2, + MODE_THREAD = 3, }; enum TaskIpcMode { @@ -655,9 +541,11 @@ class Server { }; enum ThreadType { + THREAD_NORMAL = 0, THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, + THREAD_WORKER = 4, }; enum DispatchMode { @@ -667,9 +555,9 @@ class Server { DISPATCH_IPMOD = 4, DISPATCH_UIDMOD = 5, DISPATCH_USERFUNC = 6, - DISPATCH_STREAM = 7, - DISPATCH_CO_CONN_LB, - DISPATCH_CO_REQ_LB, + DISPATCH_CO_CONN_LB = 8, + DISPATCH_CO_REQ_LB = 9, + DISPATCH_CONCURRENT_LB = 10, }; enum FactoryDispatchResult { @@ -694,6 +582,7 @@ class Server { HOOK_MANAGER_START, HOOK_MANAGER_TIMER, HOOK_PROCESS_TIMER, + HOOK_END = SW_MAX_HOOK_TYPE - 1, }; enum CloseFlag { @@ -737,6 +626,7 @@ class Server { int null_fd = -1; uint32_t max_wait_time = SW_WORKER_MAX_WAIT_TIME; + uint32_t worker_max_concurrency = UINT_MAX; /*----------------------------Reactor schedule--------------------------------*/ sw_atomic_t worker_round_id = 0; @@ -876,17 +766,26 @@ class Server { void *private_data_1 = nullptr; void *private_data_2 = nullptr; void *private_data_3 = nullptr; + void *private_data_4 = nullptr; Factory *factory = nullptr; Manager *manager = nullptr; std::vector ports; + std::vector> worker_pipes; - inline ListenPort *get_primary_port() { + ListenPort *get_primary_port() { return ports.front(); } - inline ListenPort *get_port(int _port) { + enum Mode get_mode() const { + return mode_; + }; + + /** + * This method can only be used for INET ports and cannot obtain Unix socket ports. + */ + ListenPort *get_port(int _port) const { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; @@ -895,15 +794,24 @@ class Server { return nullptr; } - inline ListenPort *get_port_by_server_fd(int server_fd) { + ListenPort *get_port(SocketType type, const char *host, int _port) const { + for (auto port : ports) { + if (port->port == _port && port->type == type && strcmp(host, port->host.c_str()) == 0) { + return port; + } + } + return nullptr; + } + + ListenPort *get_port_by_server_fd(int server_fd) const { return (ListenPort *) connection_list[server_fd].object; } - inline ListenPort *get_port_by_fd(int fd) { + ListenPort *get_port_by_fd(int fd) const { return get_port_by_server_fd(connection_list[fd].server_fd); } - inline ListenPort *get_port_by_session_id(SessionId session_id) { + ListenPort *get_port_by_session_id(SessionId session_id) const { Connection *conn = get_connection_by_session_id(session_id); if (!conn) { return nullptr; @@ -911,25 +819,26 @@ class Server { return get_port_by_fd(conn->fd); } - inline network::Socket *get_server_socket(int fd) { + network::Socket *get_server_socket(int fd) const { return connection_list[fd].socket; } - /** - * [ReactorThread] - */ - network::Socket *get_worker_pipe_socket(Worker *worker) { - return &get_thread(SwooleTG.id)->pipe_sockets[worker->pipe_master->fd]; - } - network::Socket *get_command_reply_socket() { return is_base_mode() ? get_worker(0)->pipe_master : pipe_command->get_socket(false); } + network::Socket *get_worker_pipe_master(WorkerId id) { + return get_worker(id)->pipe_master; + } + + network::Socket *get_worker_pipe_worker(WorkerId id) { + return get_worker(id)->pipe_worker; + } + /** * [Worker|Master] */ - inline network::Socket *get_reactor_pipe_socket(SessionId session_id, int reactor_id) { + network::Socket *get_reactor_pipe_socket(SessionId session_id, int reactor_id) { int pipe_index = session_id % reactor_pipe_num; /** * pipe_worker_id: The pipe in which worker. @@ -947,13 +856,15 @@ class Server { uint32_t task_max_request = 0; uint32_t task_max_request_grace = 0; std::vector> task_notify_pipes; - EventData *task_result = nullptr; + EventData *task_results = nullptr; /** - * user process + * Used for process management, saving the mapping relationship between PID and worker pointers */ - std::vector user_worker_list; std::unordered_map user_worker_map; + /** + * Shared memory, sharing state between processes + */ Worker *user_workers = nullptr; std::unordered_map commands; @@ -965,36 +876,30 @@ class Server { Worker *workers = nullptr; ServerGS *gs = nullptr; - std::unordered_set *types = nullptr; - std::unordered_set *locations = nullptr; - std::vector *http_index_files = nullptr; + std::shared_ptr> locations = nullptr; + std::shared_ptr> http_index_files = nullptr; + std::shared_ptr> http_compression_types = nullptr; -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_t reactor_thread_barrier = {}; -#endif + Barrier reactor_thread_barrier = {}; /** * temporary directory for HTTP uploaded file. */ std::string upload_tmp_dir = "/tmp"; + /** + * Write the uploaded file in form-data to disk file + */ + size_t upload_max_filesize = 0; /** * http compression level for gzip/br */ -#ifdef SW_HAVE_COMPRESSION uint8_t http_compression_level = 0; uint32_t compression_min_length; -#endif /** * master process pid */ std::string pid_file; - /** - * stream - */ - char *stream_socket_file = nullptr; - network::Socket *stream_socket = nullptr; - Protocol stream_protocol = {}; - network::Socket *last_stream_socket = nullptr; + EventData *last_task = nullptr; std::queue *buffer_pool = nullptr; @@ -1022,16 +927,16 @@ class Server { */ std::function onManagerStart; std::function onManagerStop; - std::function onWorkerError; + std::function onWorkerError; std::function onBeforeReload; std::function onAfterReload; /** * Worker Process */ std::function onPipeMessage; - std::function onWorkerStart; - std::function onWorkerStop; - std::function onWorkerExit; + std::function onWorkerStart; + std::function onWorkerStop; + std::function onWorkerExit; std::function onUserWorkerStart; /** * Connection @@ -1047,7 +952,10 @@ class Server { */ std::function onTask; std::function onFinish; - + /** + * for MessageBus + */ + std::function msg_id_generator; /** * Hook */ @@ -1076,10 +984,20 @@ class Server { void add_static_handler_location(const std::string &); void add_static_handler_index_files(const std::string &); bool select_static_handler(http_server::Request *request, Connection *conn); + void add_http_compression_type(const std::string &type); int create(); + Factory *create_base_factory(); + Factory *create_thread_factory(); + Factory *create_process_factory(); + bool create_worker_pipes(); + void destroy_base_factory(); + void destroy_thread_factory(); + void destroy_process_factory(); + int start(); - void shutdown(); + bool reload(bool reload_all_workers); + bool shutdown(); int add_worker(Worker *worker); ListenPort *add_port(SocketType type, const char *host, int port); @@ -1088,36 +1006,62 @@ class Server { bool add_command(const std::string &command, int accepted_process_types, const Command::Handler &func); Connection *add_connection(ListenPort *ls, network::Socket *_socket, int server_fd); void abort_connection(Reactor *reactor, ListenPort *ls, network::Socket *_socket); + void abort_worker(Worker *worker); + void reset_worker_counter(Worker *worker); int connection_incoming(Reactor *reactor, Connection *conn); int get_idle_worker_num(); int get_idle_task_worker_num(); - int get_task_count(); + int get_tasking_num(); - inline int get_minfd() { + TaskId get_task_id(EventData *task) { + return gs->task_workers.get_task_id(task); + } + + uint16_t get_command_id(EventData *cmd) { + return cmd->info.server_fd; + } + + EventData *get_task_result() { + return &(task_results[swoole_get_process_id()]); + } + + WorkerId get_task_src_worker_id(EventData *task) { + return gs->task_workers.get_task_src_worker_id(task); + } + + int get_minfd() { return gs->min_fd; } - inline int get_maxfd() { + int get_maxfd() { return gs->max_fd; } - inline void set_maxfd(int maxfd) { + void set_maxfd(int maxfd) { gs->max_fd = maxfd; } - inline void set_minfd(int minfd) { + void set_minfd(int minfd) { gs->min_fd = minfd; } + pid_t get_master_pid() { + return gs->master_pid; + } + + pid_t get_manager_pid() { + return gs->manager_pid; + } + void store_listen_socket(); void store_pipe_fd(UnixSocket *p); - inline const std::string &get_document_root() { + const std::string &get_document_root() { return document_root; } - inline String *get_recv_buffer(swSocket *_socket) { + String *get_recv_buffer(network::Socket *_socket) { String *buffer = _socket->recv_buffer; if (buffer == nullptr) { buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, recv_buffer_allocator); @@ -1130,28 +1074,39 @@ class Server { return buffer; } - inline uint32_t get_worker_buffer_num() { + MessageBus *get_worker_message_bus() { +#ifdef SW_THREAD + return sw_likely(is_thread_mode()) ? SwooleTG.message_bus : &message_bus; +#else + return &message_bus; +#endif + } + + uint32_t get_worker_buffer_num() { return is_base_mode() ? 1 : reactor_num + dgram_port_num; } - inline bool is_support_unsafe_events() { - if (dispatch_mode != DISPATCH_ROUND && dispatch_mode != DISPATCH_IDLE_WORKER && - dispatch_mode != DISPATCH_STREAM) { + bool is_support_unsafe_events() { + if (is_hash_dispatch_mode()) { return true; } else { return enable_unsafe_event; } } - inline bool is_process_mode() { + bool is_process_mode() { return mode_ == MODE_PROCESS; } - inline bool is_base_mode() { + bool is_base_mode() { return mode_ == MODE_BASE; } - inline bool is_enable_coroutine() { + bool is_thread_mode() { + return mode_ == MODE_THREAD; + } + + bool is_enable_coroutine() { if (is_task_worker()) { return task_enable_coroutine; } else if (is_manager()) { @@ -1161,16 +1116,20 @@ class Server { } } - inline bool is_hash_dispatch_mode() { + bool is_master_thread() { + return swoole_get_thread_type() == Server::THREAD_MASTER; + } + + bool is_hash_dispatch_mode() { return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || dispatch_mode == DISPATCH_CO_CONN_LB; } - inline bool is_support_send_yield() { + bool is_support_send_yield() { return is_hash_dispatch_mode(); } - inline bool if_require_packet_callback(ListenPort *port, bool isset) { + bool if_require_packet_callback(ListenPort *port, bool isset) { #ifdef SW_USE_OPENSSL return (port->is_dgram() && !port->ssl && !isset); #else @@ -1178,7 +1137,7 @@ class Server { #endif } - inline bool if_require_receive_callback(ListenPort *port, bool isset) { + bool if_require_receive_callback(ListenPort *port, bool isset) { #ifdef SW_USE_OPENSSL return (((port->is_dgram() && port->ssl) || port->is_stream()) && !isset); #else @@ -1186,7 +1145,11 @@ class Server { #endif } - inline Worker *get_worker(uint16_t worker_id) { + bool if_forward_message(Session *session) { + return session->reactor_id != swoole_get_process_id(); + } + + Worker *get_worker(uint16_t worker_id) { // Event Worker if (worker_id < worker_num) { return &(gs->event_workers.workers[worker_id]); @@ -1207,38 +1170,32 @@ class Server { return nullptr; } - int get_lowest_load_worker_id() { - uint32_t lowest_load_worker_id = 0; - size_t min_coroutine = workers[0].coroutine_num; - for (uint32_t i = 1; i < worker_num; i++) { - if (workers[i].coroutine_num < min_coroutine) { - min_coroutine = workers[i].coroutine_num; - lowest_load_worker_id = i; - continue; - } - } - return lowest_load_worker_id; - } - + bool kill_worker(WorkerId worker_id, bool wait_reactor); void stop_async_worker(Worker *worker); + void stop_master_thread(); + void join_heartbeat_thread(); - inline Pipe *get_pipe_object(int pipe_fd) { + Pipe *get_pipe_object(int pipe_fd) { return (Pipe *) connection_list[pipe_fd].object; } size_t get_all_worker_num() { - return worker_num + task_worker_num + get_user_worker_num(); + return get_core_worker_num() + get_user_worker_num(); } size_t get_user_worker_num() { return user_worker_list.size(); } - inline ReactorThread *get_thread(int reactor_id) { + size_t get_core_worker_num() { + return worker_num + task_worker_num; + } + + ReactorThread *get_thread(int reactor_id) { return &reactor_threads[reactor_id]; } - inline bool is_started() { + bool is_started() { return gs->start; } @@ -1246,28 +1203,53 @@ class Server { return factory != nullptr; } + bool is_running() { + return running; + } + bool is_master() { - return SwooleG.process_type == SW_PROCESS_MASTER; + return swoole_get_process_type() == SW_PROCESS_MASTER; } bool is_worker() { - return SwooleG.process_type == SW_PROCESS_WORKER; + return swoole_get_process_type() == SW_PROCESS_EVENTWORKER; + } + + bool is_event_worker() { + return is_worker(); } bool is_task_worker() { - return SwooleG.process_type == SW_PROCESS_TASKWORKER; + return swoole_get_process_type() == SW_PROCESS_TASKWORKER; } bool is_manager() { - return SwooleG.process_type == SW_PROCESS_MANAGER; + return swoole_get_process_type() == SW_PROCESS_MANAGER; } bool is_user_worker() { - return SwooleG.process_type == SW_PROCESS_USERWORKER; + return swoole_get_process_type() == SW_PROCESS_USERWORKER; + } + + bool is_worker_thread() { + return is_thread_mode() && swoole_get_thread_type() == Server::THREAD_WORKER; + } + + bool is_worker_process() { + return !is_thread_mode() && (is_worker() || is_task_worker()); } bool is_reactor_thread() { - return SwooleG.process_type == SW_PROCESS_MASTER && SwooleTG.type == Server::THREAD_REACTOR; + return swoole_get_thread_type() == Server::THREAD_REACTOR; + } + + bool is_single_worker() { + return (worker_num == 1 && task_worker_num == 0 && max_request == 0 && get_user_worker_num() == 0); + } + + bool isset_hook(enum HookType type) { + assert(type <= HOOK_END); + return hooks[type]; } bool is_sync_process() { @@ -1279,12 +1261,12 @@ class Server { } return false; } - inline bool is_shutdown() { + bool is_shutdown() { return gs->shutdown; } // can only be used in the main process - inline bool is_valid_connection(Connection *conn) { + bool is_valid_connection(Connection *conn) { return (conn && conn->socket && conn->active && conn->socket->fd_type == SW_FD_SESSION); } @@ -1316,11 +1298,11 @@ class Server { } } - inline int get_connection_fd(SessionId session_id) { + int get_connection_fd(SessionId session_id) const { return session_list[session_id % SW_SESSION_LIST_SIZE].fd; } - inline Connection *get_connection_verify_no_ssl(SessionId session_id) { + Connection *get_connection_verify_no_ssl(SessionId session_id) { Session *session = get_session(session_id); int fd = session->fd; Connection *conn = get_connection(fd); @@ -1333,7 +1315,7 @@ class Server { return conn; } - inline Connection *get_connection_verify(SessionId session_id) { + Connection *get_connection_verify(SessionId session_id) { Connection *conn = get_connection_verify_no_ssl(session_id); #ifdef SW_USE_OPENSSL if (conn && conn->ssl && !conn->ssl_ready) { @@ -1343,14 +1325,14 @@ class Server { return conn; } - inline Connection *get_connection(int fd) { + Connection *get_connection(int fd) const { if ((uint32_t) fd > max_connection) { return nullptr; } return &connection_list[fd]; } - inline Connection *get_connection_for_iterator(int fd) { + Connection *get_connection_for_iterator(int fd) { Connection *conn = get_connection(fd); if (conn && conn->active && !conn->closed) { #ifdef SW_USE_OPENSSL @@ -1363,23 +1345,14 @@ class Server { return nullptr; } - inline Connection *get_connection_by_session_id(SessionId session_id) { + Connection *get_connection_by_session_id(SessionId session_id) const { return get_connection(get_connection_fd(session_id)); } - inline Session *get_session(SessionId session_id) { + Session *get_session(SessionId session_id) { return &session_list[session_id % SW_SESSION_LIST_SIZE]; } - inline void lock() { - lock_.lock(); - } - - inline void unlock() { - lock_.unlock(); - } - - void close_port(bool only_stream_port); void clear_timer(); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -1389,7 +1362,9 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); - ResultCode call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); + void call_worker_stop_callback(Worker *worker); + void call_worker_error_callback(Worker *worker, const ExitStatus &status); + void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); std::string call_command_handler_in_master(int command_id, const std::string &msg); void call_command_callback(int64_t request_id, const std::string &result); void foreach_connection(const std::function &callback); @@ -1399,17 +1374,16 @@ class Server { #endif static int accept_command_result(Reactor *reactor, Event *event); static int close_connection(Reactor *reactor, network::Socket *_socket); - static int dispatch_task(Protocol *proto, network::Socket *_socket, const char *data, uint32_t length); + static int dispatch_task(const Protocol *proto, network::Socket *_socket, const RecvData *rdata); int send_to_connection(SendData *); ssize_t send_to_worker_from_worker(Worker *dst_worker, const void *buf, size_t len, int flags); ssize_t send_to_worker_from_worker(WorkerId id, EventData *data, int flags) { - return send_to_worker_from_worker(get_worker(id), data, sizeof(data->info) + data->info.len, flags); + return send_to_worker_from_worker(get_worker(id), data, data->size(), flags); } ssize_t send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id); - int reply_task_result(const char *data, size_t data_len, int flags, EventData *current_task); bool send(SessionId session_id, const void *data, uint32_t length); bool sendfile(SessionId session_id, const char *file, uint32_t l_file, off_t offset, size_t length); @@ -1424,19 +1398,55 @@ class Server { const std::string &msg, const Command::Callback &fn); + bool task(EventData *task, int *dst_worker_id, bool blocking = false); + bool finish(const char *data, size_t data_len, int flags, EventData *current_task); + bool task_sync(EventData *task, int *dst_worker_id, double timeout); + bool send_pipe_message(WorkerId worker_id, EventData *msg); + void init_reactor(Reactor *reactor); - void init_worker(Worker *worker); + void init_event_worker(Worker *worker); void init_task_workers(); void init_port_protocol(ListenPort *port); void init_signal_handler(); void init_ipc_max_size(); + void init_pipe_sockets(MessageBus *mb); void set_max_connection(uint32_t _max_connection); - inline uint32_t get_max_connection() { + void set_max_concurrency(uint32_t _max_concurrency) { + if (_max_concurrency == 0) { + _max_concurrency = UINT_MAX; + } + gs->max_concurrency = _max_concurrency; + } + + void set_worker_max_concurrency(uint32_t _max_concurrency) { + if (_max_concurrency == 0) { + _max_concurrency = UINT_MAX; + } + worker_max_concurrency = _max_concurrency; + } + + uint32_t get_max_connection() { return max_connection; } + uint32_t get_max_concurrency() { + return gs->max_concurrency; + } + + uint32_t get_concurrency() { + return gs->concurrency; + } + + bool is_unavailable() { + return get_concurrency() >= get_max_concurrency(); + } + + uint32_t get_worker_max_concurrency() { + return worker_max_concurrency; + } + void set_start_session_id(SessionId value) { if (value > UINT_MAX) { value = UINT_MAX; @@ -1453,34 +1463,56 @@ class Server { int schedule_worker(int fd, SendData *data); - /** - * [Manager] - */ - pid_t spawn_event_worker(Worker *worker); - pid_t spawn_user_worker(Worker *worker); - pid_t spawn_task_worker(Worker *worker); - - void kill_user_workers(); - void kill_event_workers(); - void kill_task_workers(); + size_t get_connection_num() const { + if (gs->connection_nums) { + size_t num = 0; + for (uint32_t i = 0; i < worker_num; i++) { + num += gs->connection_nums[i]; + } + return num; + } else { + return gs->connection_num; + } + } static int wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status); static void read_worker_message(ProcessPool *pool, EventData *msg); void drain_worker_pipe(); - - void check_worker_exit_status(int worker_id, const ExitStatus &exit_status); + void clean_worker_connections(Worker *worker); /** * [Worker] */ - void worker_start_callback(); - void worker_stop_callback(); + void worker_start_callback(Worker *worker); + void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); + void worker_signal_init(void); + + std::function worker_thread_start; + std::function worker_thread_join; + std::function worker_thread_get_exit_status; + + /** + * [Master] + */ + bool signal_handler_shutdown(); + bool signal_handler_child_exit(); + bool signal_handler_reload(bool reload_all_workers); + bool signal_handler_read_message(); + bool signal_handler_reopen_logger(); + static void worker_signal_handler(int signo); - static void worker_signal_init(void); + static int reactor_process_main_loop(ProcessPool *pool, Worker *worker); + static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); + static void master_signal_handler(int signo); + + int start_master_thread(Reactor *reactor); + int start_event_worker(Worker *worker); + void start_heartbeat_thread(); + const char *get_startup_error_message(); private: enum Mode mode_; @@ -1500,21 +1532,72 @@ class Server { */ uint16_t reactor_pipe_num = 0; ReactorThread *reactor_threads = nullptr; + /** + * Only used for temporarily saving pointers in add_worker() + */ + std::vector user_worker_list; int start_check(); void check_port_type(ListenPort *ls); void destroy(); - void destroy_reactor_threads(); - void destroy_reactor_processes(); - int create_reactor_processes(); - int create_reactor_threads(); int start_reactor_threads(); int start_reactor_processes(); - int start_master_thread(); - int start_event_worker(Worker *worker); - void start_heartbeat_thread(); + int start_worker_threads(); + void stop_worker_threads(); + bool reload_worker_threads(bool reload_all_workers); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); + + int get_lowest_load_worker_id() { + uint32_t lowest_load_worker_id = 0; + size_t min_coroutine = workers[0].coroutine_num; + for (uint32_t i = 1; i < worker_num; i++) { + if (workers[i].coroutine_num < min_coroutine) { + min_coroutine = workers[i].coroutine_num; + lowest_load_worker_id = i; + continue; + } + } + return lowest_load_worker_id; + } + + int get_lowest_concurrent_worker_id() { + uint32_t lowest_concurrent_worker_id = 0; + size_t min_concurrency = workers[0].concurrency; + for (uint32_t i = 1; i < worker_num; i++) { + if (workers[i].concurrency < min_concurrency) { + min_concurrency = workers[i].concurrency; + lowest_concurrent_worker_id = i; + continue; + } + } + return lowest_concurrent_worker_id; + } + + int get_idle_worker_id() { + bool found = false; + uint32_t key = 0; + SW_LOOP_N(worker_num + 1) { + key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; + if (workers[key].is_idle()) { + found = true; + break; + } + } + if (sw_unlikely(!found)) { + scheduler_warning = true; + } + swoole_trace_log(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); + return key; + } + + void lock() { + lock_.lock(); + } + + void unlock() { + lock_.unlock(); + } }; } // namespace swoole diff --git a/include/swoole_signal.h b/include/swoole_signal.h index 256d8a0ae77..5416dedce5d 100644 --- a/include/swoole_signal.h +++ b/include/swoole_signal.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -34,10 +34,10 @@ typedef swoole::SignalHandler swSignalHandler; #ifdef HAVE_SIGNALFD void swoole_signalfd_init(); -bool swoole_signalfd_setup(swoole::Reactor *reactor); #endif SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func); +SW_API bool swoole_signal_isset(int signo); SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func, int restart, int mask); SW_API swSignalHandler swoole_signal_get_handler(int signo); diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 7ede64599c2..35606ee3811 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -21,6 +21,9 @@ #include #include #include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif #include #include #include @@ -44,29 +47,26 @@ #define s6_addr32 _S6_un._S6_u32 #endif +static bool IN_IS_ADDR_LOOPBACK(struct in_addr *a) { + return a->s_addr == htonl(INADDR_LOOPBACK); +} + // OS Feature #if defined(HAVE_KQUEUE) || !defined(HAVE_SENDFILE) -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); #else #include #define swoole_sendfile(out_fd, in_fd, offset, limit) sendfile(out_fd, in_fd, offset, limit) #endif +enum { + SW_BAD_SOCKET = -1, +}; + namespace swoole { -namespace network { +struct GetaddrinfoRequest; -struct GetaddrinfoRequest { - const char *hostname; - const char *service; - int family; - int socktype; - int protocol; - int error; - void *result; - int count; - - void parse_result(std::vector &retval); -}; +namespace network { struct SendfileTask { off_t offset; @@ -97,12 +97,24 @@ struct Address { SocketType type; bool assign(SocketType _type, const std::string &_host, int _port); + bool assign(const std::string &url); + const char *get_ip() { return get_addr(); } + int get_port(); const char *get_addr(); + bool is_loopback_addr() { + if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { + return IN_IS_ADDR_LOOPBACK(&addr.inet_v4.sin_addr); + } else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) { + return IN6_IS_ADDR_LOOPBACK(&addr.inet_v6.sin6_addr); + } + return false; + } + static bool verify_ip(int __af, const std::string &str) { char tmp_address[INET6_ADDRSTRLEN]; return inet_pton(__af, str.c_str(), tmp_address) != -1; @@ -123,7 +135,7 @@ struct IOVector { void update_iterator(ssize_t __n); - inline struct iovec *get_iterator() { + struct iovec *get_iterator() { return iov_iterator; } @@ -135,15 +147,15 @@ struct IOVector { return len; } - inline int get_remain_count() { + int get_remain_count() { return remain_count; } - inline int get_index() { + int get_index() { return index; } - inline size_t get_offset_bytes() { + size_t get_offset_bytes() { return offset_bytes; } }; @@ -186,6 +198,7 @@ struct Socket { uchar skip_recv : 1; uchar recv_wait : 1; uchar event_hup : 1; + uchar dont_restart : 1; // memory buffer size [user space] uint32_t buffer_size; @@ -230,43 +243,49 @@ struct Socket { bool set_recv_timeout(double timeout); bool set_send_timeout(double timeout); - inline bool set_nonblock() { + bool set_nonblock() { return set_fd_option(1, -1); } - inline bool set_block() { + bool set_block() { return set_fd_option(0, -1); } bool set_fd_option(int _nonblock, int _cloexec); - inline int set_option(int level, int optname, int optval) { + int set_option(int level, int optname, int optval) { return setsockopt(fd, level, optname, &optval, sizeof(optval)); } - inline int set_option(int level, int optname, const void *optval, socklen_t optlen) { + int set_option(int level, int optname, const void *optval, socklen_t optlen) { return setsockopt(fd, level, optname, optval, optlen); } - inline int get_option(int level, int optname, void *optval, socklen_t *optlen) { + int get_option(int level, int optname, void *optval, socklen_t *optlen) { return getsockopt(fd, level, optname, optval, optlen); } - inline int get_option(int level, int optname, int *optval) { + int get_option(int level, int optname, int *optval) { socklen_t optlen = sizeof(*optval); return get_option(level, optname, optval, &optlen); } - inline int get_fd() { + int get_fd() { return fd; } - inline int get_name(Address *sa) { + int move_fd() { + int sock_fd = fd; + fd = SW_BAD_SOCKET; + return sock_fd; + } + + int get_name(Address *sa) { sa->len = sizeof(sa->addr); return getsockname(fd, &sa->addr.ss, &sa->len); } - inline int set_tcp_nopush(int nopush) { + int set_tcp_nopush(int nopush) { #ifdef TCP_CORK if (set_option(IPPROTO_TCP, TCP_CORK, nopush) == SW_ERR) { return -1; @@ -303,7 +322,7 @@ struct Socket { char buf; errno = 0; ssize_t retval = peek(&buf, sizeof(buf), MSG_DONTWAIT); - return !(retval == 0 || (retval < 0 && catch_error(errno) == SW_CLOSE)); + return !(retval == 0 || (retval < 0 && catch_read_error(errno) == SW_CLOSE)); } /** @@ -315,6 +334,7 @@ struct Socket { ssize_t peek(void *__buf, size_t __n, int __flags); Socket *accept(); int bind(const std::string &_host, int *port); + Socket *dup(); ssize_t readv(IOVector *io_vector); ssize_t writev(IOVector *io_vector); @@ -338,15 +358,15 @@ struct Socket { int sendfile_blocking(const char *filename, off_t offset, size_t length, double timeout); ssize_t writev_blocking(const struct iovec *iov, size_t iovcnt); - inline int connect(const Address &sa) { + int connect(const Address &sa) { return ::connect(fd, &sa.addr.ss, sa.len); } - inline int connect(const Address *sa) { + int connect(const Address *sa) { return ::connect(fd, &sa->addr.ss, sa->len); } - inline int connect(const std::string &host, int port) { + int connect(const std::string &host, int port) { Address addr; addr.assign(socket_type, host, port); return connect(addr); @@ -365,8 +385,8 @@ struct Socket { ssize_t ssl_send(const void *__buf, size_t __n); ssize_t ssl_readv(IOVector *io_vector); ssize_t ssl_writev(IOVector *io_vector); - int ssl_sendfile(const File &fp, off_t *offset, size_t size); - STACK_OF(X509) *ssl_get_peer_cert_chain(); + ssize_t ssl_sendfile(const File &fp, off_t *offset, size_t size); + STACK_OF(X509) * ssl_get_peer_cert_chain(); std::vector ssl_get_peer_cert_chain(int limit); X509 *ssl_get_peer_certificate(); int ssl_get_peer_certificate(char *buf, size_t n); @@ -379,12 +399,12 @@ struct Socket { const char *ssl_get_error_reason(int *reason); #endif - inline ssize_t recvfrom(char *__buf, size_t __len, int flags, Address *sa) { + ssize_t recvfrom(char *__buf, size_t __len, int flags, Address *sa) { sa->len = sizeof(sa->addr); return ::recvfrom(fd, __buf, __len, flags, &sa->addr.ss, &sa->len); } - inline bool cork() { + bool cork() { if (tcp_nopush) { return false; } @@ -401,7 +421,7 @@ struct Socket { return true; } - inline bool uncork() { + bool uncork() { if (!tcp_nopush) { return false; } @@ -470,6 +490,18 @@ struct Socket { return ::read(fd, __buf, __len); } + /** + * Read data from the socket synchronously without setting non-blocking or blocking IO, + * and allow interruptions by signals. + */ + ssize_t read_sync(void *__buf, size_t __len, int timeout_ms = -1); + + /** + * Write data to the socket synchronously without setting non-blocking or blocking IO, + * and allow interruptions by signals. + */ + ssize_t write_sync(const void *__buf, size_t __len, int timeout_ms = -1); + int shutdown(int __how) { return ::shutdown(fd, __how); } @@ -477,7 +509,7 @@ struct Socket { ssize_t sendto_blocking(const Address &dst_addr, const void *__buf, size_t __n, int flags = 0); ssize_t recvfrom_blocking(char *__buf, size_t __len, int flags, Address *sa); - inline ssize_t sendto(const char *dst_host, int dst_port, const void *data, size_t len, int flags = 0) const { + ssize_t sendto(const char *dst_host, int dst_port, const void *data, size_t len, int flags = 0) const { Address addr = {}; if (!addr.assign(socket_type, dst_host, dst_port)) { return SW_ERR; @@ -485,11 +517,11 @@ struct Socket { return sendto(addr, data, len, flags); } - inline ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags) const { + ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags) const { return ::sendto(fd, data, len, flags, &dst_addr.addr.ss, dst_addr.len); } - inline int catch_error(int err) const { + int catch_error(int err) const { switch (err) { case EFAULT: abort(); @@ -524,16 +556,44 @@ struct Socket { } } - static inline SocketType convert_to_type(int domain, int type, int protocol = 0) { - switch (domain) { - case AF_INET: - return type == SOCK_STREAM ? SW_SOCK_TCP : SW_SOCK_UDP; - case AF_INET6: - return type == SOCK_STREAM ? SW_SOCK_TCP6 : SW_SOCK_UDP6; - case AF_UNIX: - return type == SOCK_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM; + int catch_write_error(int err) const { + switch (err) { + case ENOBUFS: + return SW_WAIT; + default: + return catch_error(err); + } + } + + int catch_write_pipe_error(int err) { + switch (err) { + case ENOBUFS: + case EMSGSIZE: + return SW_REDUCE_SIZE; default: + return catch_error(err); + } + } + + int catch_read_error(int err) const { + return catch_error(err); + } + + static inline SocketType convert_to_type(int domain, int type) { + if (domain == AF_INET && type == SOCK_STREAM) { return SW_SOCK_TCP; + } else if (domain == AF_INET6 && type == SOCK_STREAM) { + return SW_SOCK_TCP6; + } else if (domain == AF_UNIX && type == SOCK_STREAM) { + return SW_SOCK_UNIX_STREAM; + } else if (domain == AF_INET && type == SOCK_DGRAM) { + return SW_SOCK_UDP; + } else if (domain == AF_INET6 && type == SOCK_DGRAM) { + return SW_SOCK_UDP6; + } else if (domain == AF_UNIX && type == SOCK_DGRAM) { + return SW_SOCK_UNIX_DGRAM; + } else { + return SW_SOCK_RAW; } } @@ -587,12 +647,31 @@ int gethostbyname(int type, const char *name, char *addr); int getaddrinfo(GetaddrinfoRequest *req); } // namespace network + +/** + * This function will never return NULL; if memory allocation fails, a C++ exception will be thrown. + * Must use the `socket->free()` function to release the object pointer instead of the `delete` operator. + * When the socket is released, it will close the file descriptor (fd). + * If you do not want the fd to be closed, use `socket->move_fd()` to relinquish ownership of the fd. + */ network::Socket *make_socket(int fd, FdType fd_type); +/** + * The following three functions will return a null pointer if the socket creation fails. + * It is essential to check the return value; + * if it is nullptr, you should inspect errno to determine the cause of the error. + */ network::Socket *make_socket(SocketType socket_type, FdType fd_type, int flags); +network::Socket *make_socket( + SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags); +int socket(int sock_domain, int sock_type, int socket_protocol, int flags); network::Socket *make_server_socket(SocketType socket_type, const char *address, int port = 0, int backlog = SW_BACKLOG); +/** + * Verify if the input string is an IP address, + * where AF_INET indicates an IPv4 address, such as 192.168.1.100, + * and AF_INET6 indicates an IPv6 address, for example, 2001:0000:130F:0000:0000:09C0:876A:130B. + */ bool verify_ip(int __af, const std::string &str); } // namespace swoole - diff --git a/include/swoole_socket_hook.h b/include/swoole_socket_hook.h index 97aa81742cb..bce09f4afdd 100644 --- a/include/swoole_socket_hook.h +++ b/include/swoole_socket_hook.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -35,7 +35,11 @@ extern "C" { #define recv(sockfd, buf, len, flags) swoole_coroutine_recv(sockfd, buf, len, flags) #define close(fd) swoole_coroutine_close(fd) #define connect(sockfd, addr, addrlen) swoole_coroutine_connect(sockfd, addr, addrlen) +#ifdef SW_HOOK_POLL_FAKE +#define poll(fds, nfds, timeout) swoole_coroutine_poll_fake(fds, nfds, timeout) +#else #define poll(fds, nfds, timeout) swoole_coroutine_poll(fds, nfds, timeout) +#endif #define sendmsg(sockfd, msg, flags) swoole_coroutine_sendmsg(sockfd, msg, flags) #define recvmsg(sockfd, msg, flags) swoole_coroutine_recvmsg(sockfd, msg, flags) #define getaddrinfo(name, service, req, pai) swoole_coroutine_getaddrinfo(name, service, req, pai) diff --git a/include/swoole_ssl.h b/include/swoole_ssl.h index c137bcc7e34..469c85d3270 100644 --- a/include/swoole_ssl.h +++ b/include/swoole_ssl.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -43,6 +43,15 @@ #undef SW_SUPPORT_DTLS #endif +#ifdef OPENSSL_IS_BORINGSSL +#define BIO_CTRL_DGRAM_SET_CONNECTED 32 +#define BIO_CTRL_DGRAM_SET_PEER 44 +#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45 +#define BIO_dgram_get_peer(b,peer) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_GET_PEER, 0, (char *)(peer)) +#define OPENSSL_assert(x) assert(x) +#endif + enum swSSLCreateFlag { SW_SSL_SERVER = 1, SW_SSL_CLIENT = 2, @@ -113,6 +122,11 @@ struct SSLContext { uchar disable_tls_host_name : 1; std::string tls_host_name; #endif + +#ifdef OPENSSL_IS_BORINGSSL + uint8_t grease; +#endif + std::string cafile; std::string capath; uint8_t verify_depth; diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 65d501c8005..c35d7bf78dc 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -25,42 +25,68 @@ namespace swoole { namespace http_server { - class StaticHandler { private: Server *serv; std::string request_url; std::string dir_path; - struct { + std::set dir_files; + std::string index_file; + typedef struct { off_t offset; size_t length; - char filename[PATH_MAX]; - } task; + char part_header[SW_HTTP_SERVER_PART_HEADER]; + } task_t; + std::vector tasks; - size_t l_filename; + size_t l_filename = 0; + char filename[PATH_MAX]; struct stat file_stat; - bool last; + bool last = false; + std::string content_type; + std::string boundary; + std::string end_part; + size_t content_length = 0; public: - int status_code; + int status_code = SW_HTTP_OK; StaticHandler(Server *_server, const char *url, size_t url_length) : request_url(url, url_length) { serv = _server; - task.length = 0; - task.offset = 0; - last = false; - status_code = 200; - l_filename = 0; - dir_path = ""; } + + /** + * @return true: continue to execute backwards + * @return false: break static handler + */ bool hit(); + bool hit_index_file(); + bool is_modified(const std::string &date_if_modified_since); - size_t get_index_page(std::set &index_files, char *buffer, size_t size); - bool get_dir_files(std::set &index_files); - bool set_filename(std::string &filename); + bool is_modified_range(const std::string &date_range); + size_t make_index_page(String *buffer); + bool get_dir_files(); + bool set_filename(const std::string &filename); + + bool catch_error() { + if (last) { + status_code = SW_HTTP_NOT_FOUND; + return true; + } else { + return false; + } + } + + bool has_index_file() { + return !index_file.empty(); + } + + bool is_enabled_auto_index() { + return serv->http_autoindex; + } std::string get_date(); - inline time_t get_file_mtime() { + time_t get_file_mtime() { #ifdef __MACH__ return file_stat.st_mtimespec.tv_sec; #else @@ -70,30 +96,79 @@ class StaticHandler { std::string get_date_last_modified(); - inline const char *get_filename() { - return task.filename; + const char *get_filename() { + return filename; + } + + const char *get_boundary() { + if (boundary.empty()) { + boundary = std::string(SW_HTTP_SERVER_BOUNDARY_PREKEY); + swoole_random_string(boundary, SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE - sizeof(SW_HTTP_SERVER_BOUNDARY_PREKEY)); + } + return boundary.c_str(); } - inline const char *get_mimetype() { + const char *get_content_type() { + if (tasks.size() > 1) { + content_type = std::string("multipart/byteranges; boundary=") + get_boundary(); + return content_type.c_str(); + } else { + return get_mimetype(); + } + } + + const char *get_mimetype() { return swoole::mime_type::get(get_filename()).c_str(); } - inline std::string get_filename_std_string() { - return std::string(task.filename, l_filename); + std::string get_filename_std_string() { + return std::string(filename, l_filename); } - inline size_t get_filesize() { + bool get_absolute_path(); + + size_t get_filesize() { return file_stat.st_size; } - inline const network::SendfileTask *get_task() { - return (const network::SendfileTask *) &task; + const std::vector &get_tasks() { + return tasks; } - inline bool is_dir() { + bool is_dir() { return S_ISDIR(file_stat.st_mode); } + + bool is_link() { + return S_ISLNK(file_stat.st_mode); + } + + bool is_file() { + return S_ISREG(file_stat.st_mode); + } + + bool is_absolute_path() { + return swoole_strnpos(filename, l_filename, SW_STRL("..")) == -1; + } + + bool is_located_in_document_root() { + const std::string &document_root = serv->get_document_root(); + const size_t l_document_root = document_root.length(); + + return l_filename > l_document_root && filename[l_document_root] == '/' && + swoole_str_starts_with(filename, l_filename, document_root.c_str(), l_document_root); + } + + size_t get_content_length() { + return content_length; + } + + const char *get_end_part() { + return end_part.c_str(); + } + + void parse_range(const char *range, const char *if_range); }; -}; // namespace http +}; // namespace http_server }; // namespace swoole diff --git a/include/swoole_string.h b/include/swoole_string.h index 408c66d0758..7adb7a5edeb 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -23,7 +23,16 @@ #define SW_STRINGL(s) s->str, s->length #define SW_STRINGS(s) s->str, s->size +// copy value #define SW_STRINGCVL(s) s->str + s->offset, s->length - s->offset +// append value +#define SW_STRINGAVL(s) s->str + s->length, s->size - s->length +/** + * This function does not automatically expand memory; + * ensure that the value to be written is less than the actual remaining capacity (size-length). + * If the size of the value cannot be determined, should use the String::format() function. + */ +#define SW_STRING_FORMAT(s, format, ...) s->length += sw_snprintf(SW_STRINGAVL(s), format, ##__VA_ARGS__) namespace swoole { @@ -79,11 +88,14 @@ class String { } String(const char *_str, size_t _length) { - alloc(_length, nullptr); + alloc(_length + 1, nullptr); memcpy(str, _str, _length); + str[_length] = '\0'; length = _length; } + String(const std::string &_str) : String(_str.c_str(), _str.length()) {} + String(String &_str) { alloc(_str.size, _str.allocator); memcpy(_str.str, str, _str.length); @@ -126,31 +138,31 @@ class String { } } - inline char *value() { + char *value() { return str; } - inline size_t get_length() { + size_t get_length() { return length; } - inline size_t capacity() { + size_t capacity() { return size; } - inline std::string to_std_string() { + std::string to_std_string() { return std::string(str, length); } - inline bool contains(const char *needle, size_t l_needle) { + bool contains(const char *needle, size_t l_needle) { return swoole_strnstr(str, length, needle, l_needle) != nullptr; } - inline bool contains(const std::string &needle) { + bool contains(const std::string &needle) { return contains(needle.c_str(), needle.size()); } - inline bool grow(size_t incr_value) { + bool grow(size_t incr_value) { length += incr_value; if (length == size && !reserve(size * 2)) { return false; @@ -172,41 +184,46 @@ class String { return str == nullptr || length == 0; } - inline void clear() { + void clear() { length = 0; offset = 0; } - inline bool extend() { + bool extend() { return extend(size * 2); } - inline bool extend(size_t new_size) { + bool extend(size_t new_size) { assert(new_size > size); return reserve(new_size); } - inline bool extend_align(size_t _new_size) { + bool extend_align(size_t _new_size) { size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2); while (align_size < _new_size) { align_size *= 2; } - return reserve(align_size) ; + return reserve(align_size); } bool reserve(size_t new_size); + /** + * Transfer ownership of the string content pointer to the caller, who will capture this memory. + * The caller must manage and free this memory; it will not free when the string is destructed. + */ + char *release(); bool repeat(const char *data, size_t len, size_t n); int append(const char *append_str, size_t length); - inline int append(const std::string &append_str) { + int append(const std::string &append_str) { return append(append_str.c_str(), append_str.length()); } - inline int append(char c) { + int append(char c) { return append(&c, sizeof(c)); } - inline int append(const String &append_str) { + int append(const String &append_str) { size_t new_size = length + append_str.length; if (new_size > size) { if (!reserve(new_size)) { @@ -219,10 +236,10 @@ class String { return SW_OK; } - inline void write(off_t _offset, String *write_str) { + void write(off_t _offset, String *write_str) { size_t new_length = _offset + write_str->length; if (new_length > size) { - reserve(swoole_size_align(new_length * 2, SwooleG.pagesize)); + reserve(swoole_size_align(new_length * 2, swoole_pagesize())); } memcpy(str + _offset, write_str->str, write_str->length); @@ -231,10 +248,10 @@ class String { } } - inline void write(off_t _offset, const char *write_str, size_t _length) { + void write(off_t _offset, const char *write_str, size_t _length) { size_t new_length = _offset + _length; if (new_length > size) { - reserve(swoole_size_align(new_length * 2, SwooleG.pagesize)); + reserve(swoole_size_align(new_length * 2, swoole_pagesize())); } memcpy(str + _offset, write_str, _length); @@ -243,24 +260,63 @@ class String { } } + void set_null_terminated() { + if (length == size) { + extend(length + 1); + } + str[length] = '\0'; + } + int append(int value); ssize_t split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler); int append_random_bytes(size_t length, bool base64 = false); - void print(); + void print(bool print_value = true); + + enum FormatFlag { + FORMAT_APPEND = 1 << 0, + FORMAT_GROW = 1 << 1, + }; template - inline size_t format(const char *format, Args... args) { + size_t format_impl(int flags, const char *format, Args... args) { size_t _size = sw_snprintf(nullptr, 0, format, args...); if (_size == 0) { return 0; } // store \0 terminator _size++; - if (_size > size && !reserve(_size)) { - return 0; + + size_t new_size = (flags & FORMAT_APPEND) ? length + _size : _size; + if (flags & FORMAT_GROW) { + size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2); + while (align_size < new_size) { + align_size *= 2; + } + new_size = align_size; } - return (length = sw_snprintf(str, size, format, args...)); + + size_t n; + if (flags & FORMAT_APPEND) { + if (_size > size - length && !reserve(new_size)) { + return 0; + } + n = sw_snprintf(str + length, size - length, format, args...); + length += n; + } else { + if (_size > size && !reserve(new_size)) { + return 0; + } + n = sw_snprintf(str, size, format, args...); + length = n; + } + + return n; + } + + template + size_t format(const char *format, Args... args) { + return format_impl(0, format, args...); } char *pop(size_t init_size); diff --git a/include/swoole_table.h b/include/swoole_table.h index 23258e170ac..d7b0af240dc 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -74,12 +74,12 @@ struct TableRow { struct TableIterator { size_t row_memory_size_; - uint32_t absolute_index; - uint32_t collision_index; + uint32_t absolute_index = 0; + uint32_t collision_index = 0; TableRow *current_; Mutex *mutex_; - TableIterator(size_t row_size) { + explicit TableIterator(size_t row_size) { current_ = (TableRow *) sw_malloc(row_size); if (!current_) { throw std::bad_alloc(); @@ -153,9 +153,6 @@ struct TableColumn { class Table { private: - Table() = delete; - ~Table() = delete; - std::unordered_map *column_map; Mutex *mutex; size_t size; @@ -187,8 +184,12 @@ class Table { sw_atomic_long_t update_count; uint32_t conflict_max_level; + Table() = delete; + ~Table() = delete; + static Table *make(uint32_t rows_size, float conflict_proportion); - size_t get_memory_size(); + size_t calc_memory_size() const; + size_t get_memory_size() const; uint32_t get_available_slice_num(); uint32_t get_total_slice_num(); bool create(); @@ -197,12 +198,10 @@ class Table { TableRow *get(const char *key, uint16_t keylen, TableRow **rowlock); bool del(const char *key, uint16_t keylen); void forward(); - // only release local memory of the current process - void free(); // release shared memory void destroy(); - bool is_created() { + bool is_created() const { return created; } @@ -214,18 +213,10 @@ class Table { hash_func = _fn; } - size_t get_size() { + size_t get_size() const { return size; } - int lock() { - return mutex->lock(); - } - - int unlock() { - return mutex->unlock(); - } - TableRow *get_by_index(uint32_t index) { TableRow *row = rows[index]; return row->active ? row : nullptr; @@ -240,7 +231,7 @@ class Table { } } - size_t count() { + size_t count() const { return row_num; } @@ -265,6 +256,14 @@ class Table { iterator->unlock(); } + void clear_row(TableRow *row) { + for (auto & i : *column_list) { + i->clear(row); + } + } + + private: + TableRow *hash(const char *key, int keylen) { uint64_t hashv = hash_func(key, keylen); uint64_t index = hashv & mask; @@ -272,25 +271,41 @@ class Table { return rows[index]; } - void check_key_length(uint16_t *keylen) { - if (*keylen >= SW_TABLE_KEY_SIZE) { - *keylen = SW_TABLE_KEY_SIZE - 1; - } + TableRow *alloc_row() { + lock(); + auto new_row = (TableRow *) pool->alloc(0); + unlock(); + return new_row; } - void clear_row(TableRow *row) { - for (auto i = column_list->begin(); i != column_list->end(); i++) { - (*i)->clear(row); + void free_row(TableRow *tmp) { + lock(); + tmp->clear(); + pool->free(tmp); + unlock(); + } + + static void check_key_length(uint16_t *keylen) { + if (*keylen >= SW_TABLE_KEY_SIZE) { + *keylen = SW_TABLE_KEY_SIZE - 1; } } void init_row(TableRow *new_row, const char *key, int keylen) { - sw_memset_zero(new_row, sizeof(TableRow)); + sw_memset_zero((char *) new_row + offsetof(TableRow, active), sizeof(TableRow) - offsetof(TableRow, active)); memcpy(new_row->key, key, keylen); new_row->key[keylen] = '\0'; new_row->key_len = keylen; new_row->active = 1; sw_atomic_fetch_add(&(row_num), 1); } + + int lock() { + return mutex->lock(); + } + + int unlock() { + return mutex->unlock(); + } }; } // namespace swoole diff --git a/include/swoole_thread.h b/include/swoole_thread.h new file mode 100644 index 00000000000..e3bff3a464c --- /dev/null +++ b/include/swoole_thread.h @@ -0,0 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_lock.h" + +#include + +#if defined(__linux__) +#include /* syscall(SYS_gettid) */ +#elif defined(__FreeBSD__) +#include /* pthread_getthreadid_np() */ +#elif defined(__OpenBSD__) +#include /* getthrid() */ +#elif defined(_AIX) +#include /* thread_self() */ +#elif defined(__NetBSD__) +#include /* _lwp_self() */ +#elif defined(__CYGWIN__) || defined(WIN32) +#include /* GetCurrentThreadId() */ +#endif + +static long swoole_thread_get_native_id(void) { +#ifdef __APPLE__ + uint64_t native_id; + (void) pthread_threadid_np(NULL, &native_id); +#elif defined(__linux__) + pid_t native_id = syscall(SYS_gettid); +#elif defined(__FreeBSD__) + int native_id = pthread_getthreadid_np(); +#elif defined(__OpenBSD__) + pid_t native_id = getthrid(); +#elif defined(_AIX) + tid_t native_id = thread_self(); +#elif defined(__NetBSD__) + lwpid_t native_id = _lwp_self(); +#elif defined(__CYGWIN__) || defined(WIN32) + DWORD native_id = GetCurrentThreadId(); +#endif + return native_id; +} + +static bool swoole_thread_set_name(const char *name) { +#if defined(__APPLE__) + return pthread_setname_np(name) == 0; +#else + return pthread_setname_np(pthread_self(), name) == 0; +#endif +} diff --git a/include/swoole_timer.h b/include/swoole_timer.h index 3fcb3a043e9..b41b033c69a 100644 --- a/include/swoole_timer.h +++ b/include/swoole_timer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -62,11 +62,12 @@ class Timer { /*---------------event timer--------------*/ struct timeval base_time; /*----------------------------------------*/ - int (*set)(Timer *timer, long exec_msec) = nullptr; - void (*close)(Timer *timer) = nullptr; + std::function set; + std::function close; - bool init_reactor(Reactor *reactor); - bool init_system_timer(); + bool init_with_reactor(Reactor *reactor); + bool init_with_user_scheduler(const TimerScheduler &scheduler); + bool init_with_system_timer(); public: long next_msec_; @@ -75,7 +76,7 @@ class Timer { ~Timer(); static int now(struct timeval *time); - inline int64_t get_relative_msec() { + int64_t get_relative_msec() { struct timeval _now; if (now(&_now) < 0) { return SW_ERR; @@ -95,7 +96,7 @@ class Timer { return msec1 + msec2; } - inline Reactor *get_reactor() { + Reactor *get_reactor() { return reactor_; } @@ -113,7 +114,7 @@ class Timer { void reinit(Reactor *reactor); int select(); - inline TimerNode *get(long id) { + TimerNode *get(long id) { auto it = map.find(id); if (it == map.end()) { return nullptr; @@ -122,24 +123,24 @@ class Timer { } } - inline TimerNode *get(long id, const enum TimerNode::Type type) { + TimerNode *get(long id, const enum TimerNode::Type type) { TimerNode *tnode = get(id); return (tnode && tnode->type == type) ? tnode : nullptr; } - inline size_t count() { + size_t count() { return map.size(); } - inline uint64_t get_round() { + uint64_t get_round() { return round; } - inline bool remove(long id) { + bool remove(long id) { return remove(get(id)); } - inline const std::unordered_map &get_map() { + const std::unordered_map &get_map() { return map; } }; diff --git a/include/swoole_util.h b/include/swoole_util.h index 61a2bcf5004..2aa5a9d1721 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -53,9 +53,10 @@ inline std::string vformat(const char *format, va_list args) { } } // namespace std_string +// Keep parameter 'steady' as false for backward compatibility. template static inline long time(bool steady = false) { - if (steady) { + if (sw_likely(steady)) { auto now = std::chrono::steady_clock::now(); return std::chrono::duration_cast(now.time_since_epoch()).count(); } else { @@ -64,16 +65,28 @@ static inline long time(bool steady = false) { } } +static inline long get_timezone() { +#ifdef __linux__ + return timezone; +#else + struct timezone tz; + struct timeval tv; + gettimeofday(&tv, &tz); + return tz.tz_minuteswest * 60; +#endif +} + class DeferTask { - private: + private: std::stack list_; - public: + + public: void add(Callback fn) { list_.push(fn); } ~DeferTask() { - while(!list_.empty()) { + while (!list_.empty()) { auto fn = list_.top(); fn(nullptr); list_.pop(); @@ -109,6 +122,56 @@ class ScopeGuard { bool _active; }; +class BitMap { + private: + uint64_t *array_; + size_t n_bits_; + + size_t get_array_size(size_t n_bits) { + return (((n_bits) + 63) / 64 * 8); + } + + size_t get_offset(size_t i) { + assert(i < n_bits_); + /* (i / 64) */ + return i >> 6; + } + + uint64_t to_int(size_t i, size_t offset) { + return ((uint64_t) 1) << (i - (offset << 6)); + } + + public: + BitMap(size_t n_bits) { + assert(n_bits > 0); + array_ = (uint64_t *) new uint64_t[get_array_size(n_bits)]; + n_bits_ = n_bits; + } + + ~BitMap() { + delete[] array_; + } + + void clear() { + memset(array_, 0, sizeof(uint64_t) * get_array_size(n_bits_)); + } + + void set(size_t i) { + const size_t off = get_offset(i); + array_[off] |= to_int(i, off); + } + + void unset(size_t i) { + const size_t off = get_offset(i); + array_[off] &= ~to_int(i, off); + } + + bool get(size_t i) { + const size_t off = get_offset(i); + return array_[off] & to_int(i, off); + } +}; + namespace detail { enum class ScopeGuardOnExit {}; @@ -116,7 +179,7 @@ template inline ScopeGuard operator+(ScopeGuardOnExit, Fun &&fn) { return ScopeGuard(std::forward(fn)); } -} +} // namespace detail // Helper macro #define ON_SCOPE_EXIT \ @@ -124,6 +187,18 @@ inline ScopeGuard operator+(ScopeGuardOnExit, Fun &&fn) { std::string intersection(std::vector &vec1, std::set &vec2); +static inline size_t ltrim(char **str, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + if ('\0' != **str && isspace(**str)) { + ++*str; + } else { + break; + } + } + return len - i; +} + static inline size_t rtrim(char *str, size_t len) { for (size_t i = len; i > 0;) { if (isspace(str[--i])) { @@ -155,4 +230,18 @@ static inline ssize_t substr_len(const char *str, size_t len, char separator, bo return before ? substr - str : str + len - substr - 1; } +static inline bool starts_with(const char *haystack, size_t l_haystack, const char *needle, size_t l_needle) { + if (l_needle > l_haystack) { + return false; + } + return memcmp(haystack, needle, l_needle) == 0; +} + +static inline bool ends_with(const char *haystack, size_t l_haystack, const char *needle, size_t l_needle) { + if (l_needle > l_haystack) { + return false; + } + return memcmp(haystack + l_haystack - l_needle, needle, l_needle) == 0; +} + } // namespace swoole diff --git a/include/swoole_version.h b/include/swoole_version.h index 6e6ca19bd73..0a0133f0a2f 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -18,13 +18,13 @@ #ifndef SWOOLE_VERSION_H_ #define SWOOLE_VERSION_H_ -#define SWOOLE_MAJOR_VERSION 4 -#define SWOOLE_MINOR_VERSION 8 +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "4.8.0" -#define SWOOLE_VERSION_ID 40800 -#define SWOOLE_API_VERSION_ID 0x202012a +#define SWOOLE_VERSION "6.0.0" +#define SWOOLE_VERSION_ID 60000 +#define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ "A bug occurred in Swoole-v" SWOOLE_VERSION ", please report it.\n" \ diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h index 8a8cd59ef4b..2a1f68b7f39 100644 --- a/include/swoole_websocket.h +++ b/include/swoole_websocket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,6 +29,7 @@ #define SW_WEBSOCKET_CLOSE_CODE_LEN 2 #define SW_WEBSOCKET_CLOSE_REASON_MAX_LEN 125 #define SW_WEBSOCKET_OPCODE_MAX swoole::websocket::OPCODE_PONG +#define SW_WEBSOCKET_MESSAGE_HEADER_SIZE (SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t)) namespace swoole { namespace websocket { @@ -99,6 +100,9 @@ enum CloseReason { CLOSE_MESSAGE_TOO_BIG = 1009, CLOSE_EXTENSION_MISSING = 1010, CLOSE_SERVER_ERROR = 1011, + CLOSE_SERVICE_RESTART = 1012, + CLOSE_TRY_AGAIN_LATER = 1013, + CLOSE_BAD_GATEWAY = 1014, CLOSE_TLS = 1015, }; @@ -151,8 +155,8 @@ static inline bool decode(Frame *frame, String *str) { return decode(frame, str->str, str->length); } -ssize_t get_package_length(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); -int dispatch_frame(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); +int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); } // namespace websocket } // namespace swoole diff --git a/include/swoole_wheel_timer.h b/include/swoole_wheel_timer.h index ecd437d1150..0b852c1856c 100644 --- a/include/swoole_wheel_timer.h +++ b/include/swoole_wheel_timer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/make.sh b/make.sh deleted file mode 100755 index 3aea7d6d79a..00000000000 --- a/make.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/sh -e -__DIR__=$(cd "$(dirname "$0")";pwd) -COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-json --enable-swoole-curl --enable-cares" - -if [ "$(uname | grep -i darwin)"x != ""x ]; then - CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" -else - CPU_COUNT="$(/usr/bin/nproc)" -fi -if [ -z ${CPU_COUNT} ]; then - CPU_COUNT=4 -fi - -cd "${__DIR__}" - -if [ "$1" = "cmake" ] ;then - phpize - ./configure ${COMPILE_PARAMS} - cmake . - make -j ${CPU_COUNT} - exit 0 -fi - -if [ "$1" = "clean" ] ;then - make clean - phpize --clean - exit 0 -fi - -if [ "$1" = "install-module" ] ;then - make ext-swoole - __EXT_DIR__=$(php-config --extension-dir) - cp lib/swoole.so "${__EXT_DIR__}" - echo "cp lib/swoole.so ${__EXT_DIR__}" - exit 0 -fi - -if [ "$1" = "library" ] ;then - set -e - cd ${__DIR__} - set +e - echo "rm ext-src/php_swoole.lo" - rm -f ext-src/php_swoole.lo - echo "rm ext-src/php_swoole_library.h" - rm -f ext-src/php_swoole_library.h - set -e - - if [ "$2" = "dev" ] ;then - /usr/bin/env php tools/build-library.php dev - else - /usr/bin/env php tools/build-library.php - fi - - echo "remake..." - make - echo "done" - exit 0 -fi - -if [ "$1" = "help" ] ;then - echo "./make.sh cmake" - echo "./make.sh install-module" - echo "./make.sh clean" - echo "./make.sh debug" - echo "./make.sh trace" - echo "./make.sh library [dev]" - echo "./make.sh" - exit 0 -fi - -phpize -if [ "$1" = "debug" ] ;then - ./configure ${COMPILE_PARAMS} --enable-debug-log -elif [ "$1" = "trace" ] ;then - ./configure ${COMPILE_PARAMS} --enable-trace-log -else - ./configure ${COMPILE_PARAMS} -fi -make clean -make -j ${CPU_COUNT} - -if [ "$(whoami)" = "root" ]; then - make install -else - sudo make install -fi diff --git a/mascot.png b/mascot.png deleted file mode 100644 index 2cb36e88f88..00000000000 Binary files a/mascot.png and /dev/null differ diff --git a/package.xml b/package.xml index 69856a2c600..c1cda6df0be 100644 --- a/package.xml +++ b/package.xml @@ -1,9 +1,9 @@ - swoole - pecl.php.net - Event-driven asynchronous and concurrent networking engine with high performance for PHP. - + swoole + pecl.php.net + Event-driven asynchronous and concurrent networking engine with high performance for PHP. + Event-driven asynchronous and concurrent networking engine with high performance for PHP. - event-driven - coroutine @@ -14,8 +14,6 @@ - millisecond timer - built-in tcp/http/websocket/http2 server - coroutine tcp/http/websocket client - - coroutine mysql client - - coroutine redis client - coroutine read/write file system - coroutine dns lookup - support IPv4/IPv6/UnixSocket/TCP/UDP @@ -51,11 +49,11 @@ doubaokun@php.net yes - 2021-10-12 - + 2024-12-16 + - 4.8.0 - 4.0 + 6.0.0 + 6.0 stable @@ -63,55 +61,128 @@ Apache2.0 - New APIs - --- - * Added Co::getStackUsage() (#4398) (@matyhtf) (@twose) - * Added Coroutine\Redis some api (#4390) (@chrysanthemum) - * Added Table::stats() (#4405) (@matyhtf) - * Added Coroutine::join() (#4406) (@matyhtf) - - New feature - --- - * Supported server command (#4389) (@matyhtf) - * Supported Server::onBeforeShutdown callback (#4415) (@matyhtf) - - Enhancement - --- - * Set error code when websocket pack fails (swoole/swoole-src@d27c5a5) (@matyhtf) - * Added Timer::exec_count field (#4402) (@matyhtf) - * Supported for hooked mkdir using open_basedir ini config (#4407) (@NathanFreeman) - * Added vendor_init.php (swoole/library@6c40b02) (@matyhtf) - * Supported CURLOPT_UNIX_SOCKET_PATH for SWOOLE_HOOK_CURL (swoole/library#121) (@sy-records) - * Supported ssl_ciphers config for client (#4432) (@amuluowin) - - Fixed - --- - * Fixed unnecessary URL decode of file names when uploading files (swoole/swoole-src@a73780e) (@matyhtf) - * Fixed HTTP2 max_frame_size error (#4394) (@twose) - * Fixed curl_multi_select bug #4393 (#4418) (@matyhtf) - * Fixed missing coroutine options (#4425) (@sy-records) - * Fixed connection cannot be forcibly closed when the send buffer is full (swoole/swoole-src@2198378) (@matyhtf) + - Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. + - Added a new thread class `Swoole\Thread`. @matyhtf + - Introduced thread lock `Swoole\Thread\Lock`. @matyhtf + - Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf + - Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf + - 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 + - Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman + - Added `Swoole\Thread\Map::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf + - Added `Swoole\Process::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setName()` method. @matyhtf + - Added `Swoole\Thread::setAffinity()` method. @matyhtf + - Added `Swoole\Thread::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setPriority()` method. @matyhtf + - Added `Swoole\Thread::getPriority()` method. @matyhtf + - Added `Swoole\Thread::gettid()` method. + - The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman + - Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman + - Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman + - Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf + - Added new function and class to set cookies. @matyhtf @NathanFreeman + - Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman + - `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf + - `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf + - Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf + - Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf + - 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 + - Fixed the issue where installation via `pecl` was not possible. @remicollet + - Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman + - Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman + - Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf + - Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf + - Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman + - Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward + - Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf + - Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf + - Fixed the occasional unreachable branch issue during file upload. @NathanFreeman + - Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman + - Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk + - Capture exceptions when thread creation fails. @matyhtf + - Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk + - Fixed the fatal compile error with `GCC 14`. @remicollet + - Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng + - Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman + - Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf + - Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf + - Fixed incorrect `content-type` when uploading files. @matyhtf + - Fixed code errors in the `http2` coroutine client. @matyhtf + - Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad + - Fixed errors related to `brotli` in `config.m4`. @fundawang + - Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf + - Fixed compilation errors in the `macos` environment. @matyhtf + - Fixed the issue of threads not being able to exit safely. @matyhtf + - 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 + - Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf + - Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet + - Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf + - Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf + - Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf + - Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf + - Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf + - Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf + - Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf + - Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + - Removed unnecessary checks for `socket structs`. @petk + - Upgraded Swoole Library. @deminy + - Added support for status code 451 in `Swoole\Http\Response`. @abnegate + - Synchronized `file` operation code across different PHP versions. @NathanFreeman + - Synchronized `pdo` operation code across different PHP versions. @NathanFreeman + - Optimized the code for `Socket::ssl_recv()`. @matyhtf + - Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman + - Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman + - Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf + - Optimized some fundamental coroutine logic. @matyhtf + - Upgraded the Oracle database version for CI testing. @gvenzl + - Optimized the underlying logic of `sendfile`. @matyhtf + - Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk + - Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf + - Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf + - Enhanced the HTTP client to accept duplicate request headers. @matyhtf + - Optimized `Swoole\Http\Response::write()`. @matyhtf + - `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf + - Compatible with `PHP 8.4`. @matyhtf @NathanFreeman + - Added the ability for asynchronous writing at the underlying socket level. @matyhtf + - Optimized `Swoole\Http\Response`. @NathanFreeman + - Improved underlying error messages. @matyhtf + - Supported sharing PHP native sockets in multi-threaded mode. @matyhtf + - Optimized static file service and fixed static file path error issues. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf + - Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman + - Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman + - Optimized timers so that synchronous processes do not depend on signals. @matyhtf + - Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + - No longer supports `PHP 8.0`. + - No longer supports `Swoole\Coroutine\MySQL` coroutine client. + - No longer supports `Swoole\Coroutine\Redis` coroutine client. + - No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. + - Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. - - - - - - - + + + + + + + + @@ -121,8 +192,7 @@ - - + @@ -134,6 +204,7 @@ + @@ -156,6 +227,7 @@ + @@ -164,27 +236,37 @@ + + + + + + + + + + + - - + + + + - - - @@ -228,6 +310,7 @@ + @@ -245,6 +328,7 @@ + @@ -303,10 +387,8 @@ - - @@ -315,54 +397,43 @@ - - - - + - - - + + - + - - - + + - - - - - - + - @@ -388,54 +459,56 @@ + - - - - - + + + - - - - - + - + + + + + + + + @@ -443,12 +516,9 @@ - - - @@ -474,10 +544,10 @@ + - @@ -494,22 +564,35 @@ - + + + + + + + + + + + + + + + + + + + - - - - - @@ -523,6 +606,7 @@ + @@ -532,43 +616,131 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - + + + + + + + + - @@ -595,10 +767,12 @@ + + @@ -616,15 +790,37 @@ + - - + + + + + + + + + + + + + + + + + + + + + + + @@ -637,9 +833,12 @@ + + + @@ -666,10 +865,10 @@ - + @@ -684,22 +883,25 @@ - + + - + + + @@ -712,6 +914,7 @@ + @@ -741,15 +944,17 @@ + + - + @@ -765,6 +970,7 @@ + @@ -794,6 +1000,8 @@ + + @@ -846,10 +1054,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -859,6 +1091,7 @@ + @@ -871,11 +1104,14 @@ + + + @@ -894,8 +1130,12 @@ + + + + @@ -913,6 +1153,7 @@ + @@ -924,6 +1165,11 @@ + + + + + @@ -961,6 +1207,7 @@ + @@ -971,15 +1218,25 @@ + + + + + + + + + + @@ -994,6 +1251,10 @@ + + + + @@ -1039,6 +1300,7 @@ + @@ -1049,9 +1311,8 @@ - - + @@ -1061,6 +1322,7 @@ + @@ -1068,11 +1330,11 @@ - + @@ -1100,7 +1362,10 @@ + + + @@ -1130,6 +1395,7 @@ + @@ -1149,11 +1415,23 @@ + + + + + + + + + + + + @@ -1161,21 +1439,24 @@ + + + + - - - - - - + + + + + @@ -1196,13 +1477,16 @@ + + + @@ -1210,6 +1494,7 @@ + @@ -1232,15 +1517,23 @@ + + + + + + + + @@ -1252,6 +1545,7 @@ + @@ -1261,6 +1555,7 @@ + @@ -1270,6 +1565,7 @@ + @@ -1303,11 +1599,16 @@ + + + + + @@ -1316,6 +1617,12 @@ + + + + + + @@ -1325,22 +1632,29 @@ + + + + + + + @@ -1350,33 +1664,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -1387,20 +1725,31 @@ + + + + + + + + + + + @@ -1411,9 +1760,11 @@ + + @@ -1422,6 +1773,7 @@ + @@ -1430,101 +1782,168 @@ + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + @@ -1535,70 +1954,34 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + @@ -1606,8 +1989,9 @@ - + + @@ -1641,21 +2025,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + @@ -1687,6 +2123,10 @@ + + + + @@ -1703,6 +2143,7 @@ + @@ -1718,6 +2159,7 @@ + @@ -1763,7 +2205,6 @@ - @@ -1771,9 +2212,14 @@ + + + + + @@ -1785,15 +2231,18 @@ + - - - + + + + + @@ -1806,8 +2255,6 @@ - - @@ -1867,6 +2314,7 @@ + @@ -1902,9 +2350,17 @@ + + + + + + + + @@ -1939,6 +2395,7 @@ + @@ -1952,6 +2409,7 @@ + @@ -1962,14 +2420,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1982,14 +2491,17 @@ + + + @@ -1997,7 +2509,9 @@ + + @@ -2010,62 +2524,35 @@ - + + + + - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - @@ -2086,6 +2573,7 @@ + @@ -2099,12 +2587,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2117,6 +2641,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2124,10 +2701,11 @@ - + + @@ -2136,25 +2714,14 @@ + - - - - - - - - - - - - - 7.2.0 + 8.1.0 1.4.0 @@ -2163,11 +2730,18 @@ swoole - + - - + + + + + + + + + diff --git a/php_swoole.h b/php_swoole.h index fb3437c4fbf..ce78ee064b6 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -10,13 +10,17 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #ifndef PHP_SWOOLE_H #define PHP_SWOOLE_H +#ifdef __cplusplus +extern "C" { +#endif + #include "php.h" #include "php_ini.h" #include "php_globals.h" @@ -29,11 +33,18 @@ #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_exceptions.h" +#if PHP_VERSION_ID >= 80200 +#include "zend_attributes.h" +#endif #ifdef HAVE_CONFIG_H #include "config.h" #endif +#ifdef __cplusplus +} +#endif + extern zend_module_entry swoole_module_entry; #define phpext_swoole_ptr &swoole_module_entry @@ -48,11 +59,12 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool display_errors; zend_bool cli; zend_bool use_shortname; - zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; + zend_bool enable_fiber_mock; long socket_buffer_size; int req_status; + HashTable *in_autoload; ZEND_END_MODULE_GLOBALS(swoole) // clang-format on diff --git a/travis/.gitignore b/scripts/.gitignore similarity index 100% rename from travis/.gitignore rename to scripts/.gitignore diff --git a/clear.sh b/scripts/clear.sh similarity index 85% rename from clear.sh rename to scripts/clear.sh index 2a1cfc2f425..b1b15a6ea12 100755 --- a/clear.sh +++ b/scripts/clear.sh @@ -1,8 +1,9 @@ #!/bin/sh __DIR__=$(cd "$(dirname "$0")";pwd) +__SRC_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) set -e -cd "${__DIR__}" +cd "${__SRC_DIR__}" set +e find . \( -name \*.gcno -o -name \*.gcda \) -print0 | xargs -0 rm -f find . \( -name \*.lo -o -name \*.o \) -print0 | xargs -0 rm -f diff --git a/scripts/code-format.sh b/scripts/code-format.sh new file mode 100755 index 00000000000..7e5feb03c9c --- /dev/null +++ b/scripts/code-format.sh @@ -0,0 +1,42 @@ +#!/bin/sh +__DIR__=$(cd "$(dirname "$0")";pwd) +__SRC_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) + +cd $__SRC_DIR__ + +## core source file +clang-format -i src/core/*.cc +clang-format -i src/coroutine/*.cc +clang-format -i src/lock/*.cc +clang-format -i src/memory/*.cc +clang-format -i src/network/*.cc +clang-format -i src/os/*.cc +clang-format -i src/pipe/*.cc +clang-format -i src/protocol/*.cc +clang-format -i src/reactor/*.cc +clang-format -i src/server/*.cc +clang-format -i src/wrapper/*.cc +## core header file +clang-format -i include/*.h + +## ext source file +clang-format -i *.cc +clang-format -i *.h + +## examples +clang-format -i examples/cpp/*.cc + +## core-tests source file +clang-format -i core-tests/src/_lib/*.cpp +clang-format -i core-tests/src/client/*.cpp +clang-format -i core-tests/src/core/*.cpp +clang-format -i core-tests/src/coroutine/*.cpp +clang-format -i core-tests/src/lock/*.cpp +clang-format -i core-tests/src/memory/*.cpp +clang-format -i core-tests/src/network/*.cpp +clang-format -i core-tests/src/os/*.cpp +clang-format -i core-tests/src/process/*.cpp +clang-format -i core-tests/src/protocol/*.cpp +clang-format -i core-tests/src/reactor/*.cpp +clang-format -i core-tests/src/server/*.cpp +clang-format -i core-tests/src/main.cpp diff --git a/scripts/code-stats.sh b/scripts/code-stats.sh new file mode 100755 index 00000000000..eed0fce4386 --- /dev/null +++ b/scripts/code-stats.sh @@ -0,0 +1,7 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +# enter the dir +cd "${__DIR__}" +cloc ../ --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git diff --git a/travis/debug/swoole_info.php b/scripts/debug/swoole_info.php similarity index 100% rename from travis/debug/swoole_info.php rename to scripts/debug/swoole_info.php diff --git a/scripts/debug/swoole_table_implements.php b/scripts/debug/swoole_table_implements.php new file mode 100644 index 00000000000..57e2e0e408b --- /dev/null +++ b/scripts/debug/swoole_table_implements.php @@ -0,0 +1,4 @@ + ./instantclient/network/admin/sqlnet.ora +mv ./instantclient /usr/local/ +echo '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf +ldconfig + +wget https://github.com/axboe/liburing/archive/refs/tags/liburing-2.6.tar.gz +tar zxf liburing-2.6.tar.gz +cd liburing-liburing-2.6 && ./configure && make -j$(cat /proc/cpuinfo | grep processor | wc -l) && make install + diff --git a/scripts/make.sh b/scripts/make.sh new file mode 100755 index 00000000000..d548fe9f40a --- /dev/null +++ b/scripts/make.sh @@ -0,0 +1,105 @@ +#!/bin/sh +__CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) +__DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) +__HAVE_ZTS__=$(php -v|grep ZTS) + +COMPILE_PARAMS="--enable-openssl \ +--enable-sockets \ +--enable-mysqlnd \ +--enable-swoole-curl \ +--enable-cares \ +--enable-swoole-pgsql \ +--with-swoole-odbc=unixODBC,/usr \ +--enable-swoole-sqlite" + +if [ -n "$__HAVE_ZTS__" ]; then + COMPILE_PARAMS="$COMPILE_PARAMS --enable-swoole-thread" +fi + +if [ "$(uname)" = "Linux" ]; then + COMPILE_PARAMS="$COMPILE_PARAMS --enable-iouring" +fi + +if [ "$(uname | grep -i darwin)"x != ""x ]; then + CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" +else + CPU_COUNT="$(/usr/bin/nproc)" +fi +if [ -z ${CPU_COUNT} ]; then + CPU_COUNT=4 +fi + +cd "${__DIR__}" + +if [ "$1" = "cmake" ] ;then + phpize + ./configure ${COMPILE_PARAMS} + cmake . + make -j ${CPU_COUNT} + make install + exit 0 +fi + +if [ "$1" = "clean" ] ;then + make clean + phpize --clean + exit 0 +fi + +if [ "$1" = "install-module" ] ;then + make ext-swoole + __EXT_DIR__=$(php-config --extension-dir) + cp lib/swoole.so "${__EXT_DIR__}" + echo "cp lib/swoole.so ${__EXT_DIR__}" + exit 0 +fi + +if [ "$1" = "library" ] ;then + set -e + cd ${__DIR__} + set +e + echo "rm ext-src/php_swoole.lo" + rm -f ext-src/php_swoole.lo + echo "rm ext-src/php_swoole_library.h" + rm -f ext-src/php_swoole_library.h + set -e + + if [ "$2" = "dev" ] ;then + /usr/bin/env php tools/build-library.php dev + else + /usr/bin/env php tools/build-library.php + fi + + echo "remake..." + make + echo "done" + exit 0 +fi + +if [ "$1" = "help" ] ;then + echo "./make.sh cmake" + echo "./make.sh install-module" + echo "./make.sh clean" + echo "./make.sh debug" + echo "./make.sh trace" + echo "./make.sh library [dev]" + echo "./make.sh" + exit 0 +fi + +phpize + +if [ "$1" = "debug" ] ;then + ./configure ${COMPILE_PARAMS} --enable-debug-log +elif [ "$1" = "trace" ] ;then + ./configure ${COMPILE_PARAMS} --enable-trace-log +elif [ "$1" = "config" ] ;then + ./configure ${COMPILE_PARAMS} + exit 0 +else + ./configure ${COMPILE_PARAMS} +fi + +make clean +make -j ${CPU_COUNT} +make install diff --git a/scripts/pecl-install.sh b/scripts/pecl-install.sh new file mode 100755 index 00000000000..d13fa0fffd6 --- /dev/null +++ b/scripts/pecl-install.sh @@ -0,0 +1,12 @@ +#!/bin/sh -e +__CURRENT__=`pwd` +__DIR__=$(cd "$(dirname "$0")";pwd) + +cd ${__DIR__} && cd ../ && \ +pecl config-show && \ +php tools/pecl-package.php && package_file="`ls | grep swoole-*tgz`" && \ +echo "\n" | pecl install -f ${package_file} | tee pecl.log && \ +cat pecl.log | grep "successfully" && \ +php -d extension=swoole --ri swoole && \ +pecl uninstall swoole && \ +rm -f pecl.log diff --git a/scripts/rename.php b/scripts/rename.php new file mode 100644 index 00000000000..dcd9d6838cb --- /dev/null +++ b/scripts/rename.php @@ -0,0 +1,17 @@ +&1 | grep "version"`"x = ""x ]; then + echo "\n❌ Docker not found!" + exit 1 + elif [ "`docker ps 2>&1 | grep Cannot`"x != ""x ]; then + echo "\n❌ Docker is not running!" + exit 1 + else + which "docker-compose" > /dev/null + if [ $? -ne 0 ]; then + echo "\n🤔 Can not found docker-compose, try to install it now...\n" + curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose && \ + chmod +x docker-compose && \ + sudo mv docker-compose /usr/local/bin + + which "docker-compose" > /dev/null + if [ $? -ne 0 ]; then + echo "\n❌ Install docker-compose failed!" + exit 1 + fi + + docker -v && docker-compose -v + fi + fi +} + +prepare_data_files(){ + cd ${__DIR__} && \ + remove_data_files && \ + mkdir -p \ + data \ + data/run \ + data/mysql data/run/mysqld \ + data/redis data/run/redis && \ + chmod -R 777 data + if [ $? -ne 0 ]; then + echo "\n❌ Prepare data files failed!" + exit 1 + fi +} + +remove_data_files(){ + cd ${__DIR__} && \ + rm -rf scripts/data +} + +start_docker_containers(){ + remove_docker_containers + cd ${__DIR__} && \ + docker-compose up -d && \ + docker ps -a + if [ $? -ne 0 ]; then + echo "\n❌ Create containers failed!" + exit 1 + fi +} + +remove_docker_containers(){ + cd ${__DIR__} && \ + docker-compose kill > /dev/null 2>&1 && \ + docker-compose rm -f > /dev/null 2>&1 +} + +run_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run tests failed! ExitCode: $code" + exit 1 + fi +} + +run_thread_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-thread-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run thread tests failed! ExitCode: $code" + exit 1 + fi +} + +run_iouring_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-iouring-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run iouring tests failed! ExitCode: $code" + exit 1 + fi +} + +remove_tests_resources(){ + remove_docker_containers + remove_data_files +} + +check_docker_dependency + +echo "\n📖 Prepare for files...\n" +prepare_data_files + +echo "📦 Start docker containers...\n" +start_docker_containers # && trap "remove_tests_resources" + +echo "\n⏳ Run tests in docker...\n" + +if [ "$SWOOLE_THREAD" = 1 ]; then + run_thread_tests_in_docker +elif [ "$SWOOLE_USE_IOURING" = 1 ]; then + run_iouring_tests_in_docker +else + run_tests_in_docker +fi + +echo "\n🚀🚀🚀Completed successfully🚀🚀🚀\n" diff --git a/travis/run-tests.sh b/scripts/run-tests.sh similarity index 90% rename from travis/run-tests.sh rename to scripts/run-tests.sh index 6ae34be8ba8..7226694273f 100755 --- a/travis/run-tests.sh +++ b/scripts/run-tests.sh @@ -9,8 +9,9 @@ cd ${__DIR__} && cd ../tests/ # initialization echo "" && echo "⭐️ Initialization for tests..." && echo "" -./init +php ./init cd ./include/lib +echo "composer update" composer update cd - echo "" @@ -58,9 +59,14 @@ echo "" && echo "🌵️️ Current branch is ${SWOOLE_BRANCH}" && echo "" if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then dir="base" options="${options} -m" +elif [ "$SWOOLE_THREAD" = 1 ]; then + dir="swoole_thread" +elif [ "$SWOOLE_USE_IOURING" = 1 ]; then + dir="swoole_runtime/file_hook swoole_iouring" else dir="swoole_*" fi +echo "${dir}" echo "${dir}" > tests.list for i in 1 2 3 4 5 do diff --git a/travis/simple-compile-on-github.sh b/scripts/simple-compile-on-github.sh similarity index 88% rename from travis/simple-compile-on-github.sh rename to scripts/simple-compile-on-github.sh index e1d51643ded..a67e0723adc 100755 --- a/travis/simple-compile-on-github.sh +++ b/scripts/simple-compile-on-github.sh @@ -17,7 +17,7 @@ cd "${__DIR__}" && cd ../ && \ ./clear.sh > /dev/null && \ phpize --clean > /dev/null && \ phpize > /dev/null && \ -./configure --enable-openssl --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-json --enable-swoole-curl --enable-cares > /dev/null && \ +./configure --enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares > /dev/null && \ make -j8 > /dev/null | tee /tmp/compile.log && \ (test "`cat /tmp/compile.log`"x = ""x || exit 255) && \ make install && \ diff --git a/travis/simple-compile.sh b/scripts/simple-compile.sh similarity index 82% rename from travis/simple-compile.sh rename to scripts/simple-compile.sh index 5376d0f857b..6a9549ec80d 100755 --- a/travis/simple-compile.sh +++ b/scripts/simple-compile.sh @@ -2,11 +2,6 @@ __CURRENT__=`pwd` __DIR__=$(cd "$(dirname "$0")";pwd) -if [ "${TRAVIS}"x = ""x ]; then - echo "\n❌ This script is just for Travis!" - exit 255 -fi - cd ${__DIR__} && cd ../ && \ ./clear.sh > /dev/null && \ phpize --clean > /dev/null && \ diff --git a/src/core/base.cc b/src/core/base.cc index af2a4b7d9d5..d5ab76f905c 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -41,15 +41,37 @@ #include "swoole_async.h" #include "swoole_c_api.h" #include "swoole_coroutine_c_api.h" +#include "swoole_coroutine_system.h" #include "swoole_ssl.h" +#if defined(__APPLE__) && defined(HAVE_CCRANDOMGENERATEBYTES) +#include +#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) || \ + (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) +#define OPENSSL_APPLE_CRYPTO_RANDOM 1 +#include +#include +#endif +#endif + +using swoole::NameResolver; using swoole::String; +using swoole::coroutine::System; #ifdef HAVE_GETRANDOM #include #else static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { -#ifdef HAVE_ARC4RANDOM +#if defined(HAVE_CCRANDOMGENERATEBYTES) + /* + * arc4random_buf on macOs uses ccrng_generate internally from which + * the potential error is silented to respect the portable arc4random_buf interface contract + */ + if (CCRandomGenerateBytes(buffer, size) == kCCSuccess) { + return size; + } + return -1; +#elif defined(HAVE_ARC4RANDOM) arc4random_buf(buffer, size); return size; #else @@ -75,20 +97,12 @@ static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { #endif swoole::Global SwooleG = {}; -__thread swoole::ThreadGlobal SwooleTG = {}; +thread_local swoole::ThreadGlobal SwooleTG = {}; +thread_local char sw_error[SW_ERROR_MSG_SIZE]; +std::mutex sw_thread_lock; -static std::unordered_map functions; static swoole::Logger *g_logger_instance = nullptr; -#ifdef __MACH__ -static __thread char _sw_error_buf[SW_ERROR_MSG_SIZE]; -char *sw_error_() { - return _sw_error_buf; -} -#else -__thread char sw_error[SW_ERROR_MSG_SIZE]; -#endif - static void swoole_fatal_error_impl(int code, const char *format, ...); swoole::Logger *sw_logger() { @@ -126,9 +140,9 @@ static void bug_report_message_init() { #ifdef SW_USE_OPENSSL SwooleG.bug_report_message += swoole_ssl_get_version_message(); - #endif } + void swoole_init(void) { if (SwooleG.init) { return; @@ -139,6 +153,7 @@ void swoole_init(void) { SwooleG.running = 1; SwooleG.init = 1; + SwooleG.enable_coroutine = 1; SwooleG.std_allocator = {malloc, calloc, realloc, free}; SwooleG.fatal_error = swoole_fatal_error_impl; SwooleG.cpu_num = SW_MAX(1, sysconf(_SC_NPROCESSORS_ONLN)); @@ -167,7 +182,6 @@ void swoole_init(void) { // init global shared memory SwooleG.memory_pool = new swoole::GlobalMemory(SW_GLOBAL_MEMORY_PAGESIZE, true); SwooleG.max_sockets = SW_MAX_SOCKETS_DEFAULT; - SwooleG.max_concurrency = 0; struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0) { swoole_sys_warning("getrlimit() failed"); @@ -185,7 +199,6 @@ void swoole_init(void) { // init signalfd #ifdef HAVE_SIGNALFD swoole_signalfd_init(); - SwooleG.use_signalfd = 1; SwooleG.enable_signalfd = 1; #endif @@ -195,36 +208,18 @@ void swoole_init(void) { SW_EXTERN_C_BEGIN -SW_API int swoole_add_function(const char *name, void *func) { - std::string _name(name); - auto iter = functions.find(_name); - if (iter != functions.end()) { - swoole_warning("Function '%s' has already been added", name); - return SW_ERR; - } else { - functions.emplace(std::make_pair(_name, func)); - return SW_OK; - } -} - -SW_API void *swoole_get_function(const char *name, uint32_t length) { - auto iter = functions.find(std::string(name, length)); - if (iter != functions.end()) { - return iter->second; - } else { - return nullptr; - } -} - SW_API int swoole_add_hook(enum swGlobalHookType type, swHookFunc func, int push_back) { + assert(type <= SW_GLOBAL_HOOK_END); return swoole::hook_add(SwooleG.hooks, type, func, push_back); } SW_API void swoole_call_hook(enum swGlobalHookType type, void *arg) { + assert(type <= SW_GLOBAL_HOOK_END); swoole::hook_call(SwooleG.hooks, type, arg); } SW_API bool swoole_isset_hook(enum swGlobalHookType type) { + assert(type <= SW_GLOBAL_HOOK_END); return SwooleG.hooks[type] != nullptr; } @@ -260,6 +255,13 @@ void swoole_clean(void) { delete SwooleTG.buffer_stack; SwooleTG.buffer_stack = nullptr; } + SW_LOOP_N(SW_MAX_HOOK_TYPE) { + if (SwooleG.hooks[i]) { + auto hooks = static_cast *>(SwooleG.hooks[i]); + delete hooks; + } + } + swoole_signal_clear(); SwooleG = {}; } @@ -269,7 +271,21 @@ SW_API void swoole_set_log_level(int level) { } } -SW_API void swoole_set_trace_flags(int flags) { +SW_API int swoole_get_log_level() { + if (sw_logger()) { + return sw_logger()->get_level(); + } else { + return SW_LOG_NONE; + } +} + +SW_API void swoole_set_log_file(const char *file) { + if (sw_logger()) { + sw_logger()->open(file); + } +} + +SW_API void swoole_set_trace_flags(long flags) { SwooleG.trace_flags = flags; } @@ -323,13 +339,27 @@ bool swoole_set_task_tmpdir(const std::string &dir) { return true; } +pid_t swoole_fork_exec(const std::function &fn) { + pid_t pid = fork(); + switch (pid) { + case -1: + return false; + case 0: + fn(); + exit(0); + default: + break; + } + return pid; +} + pid_t swoole_fork(int flags) { if (!(flags & SW_FORK_EXEC)) { if (swoole_coroutine_is_in()) { swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, "must be forked outside the coroutine"); } if (SwooleTG.async_threads) { - swoole_trace("aio_task_num=%d, reactor=%p", SwooleTG.async_threads->task_num, sw_reactor()); + swoole_trace("aio_task_num=%lu, reactor=%p", SwooleTG.async_threads->task_num, sw_reactor()); swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, "can not create server after using async file operation"); } @@ -373,12 +403,29 @@ pid_t swoole_fork(int flags) { * reset signal handler */ swoole_signal_clear(); + + if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_FORK)) { + swoole_call_hook(SW_GLOBAL_HOOK_AFTER_FORK, nullptr); + } } return pid; } -#ifdef SW_DEBUG +void swoole_thread_init(void) { + if (!SwooleTG.buffer_stack) { + SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); + } + swoole_signal_block_all(); +} + +void swoole_thread_clean(void) { + if (SwooleTG.buffer_stack) { + delete SwooleTG.buffer_stack; + SwooleTG.buffer_stack = nullptr; + } +} + void swoole_dump_ascii(const char *data, size_t size) { for (size_t i = 0; i < size; i++) { printf("%u ", (unsigned) data[i]); @@ -412,7 +459,6 @@ void swoole_dump_hex(const char *data, size_t outlen) { } printf("\n"); } -#endif /** * Recursive directory creation @@ -519,7 +565,7 @@ ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes) { #endif int swoole_rand(int min, int max) { - static int _seed = 0; + static time_t _seed = 0; assert(max > min); if (_seed == 0) { @@ -753,12 +799,13 @@ char *swoole_string_format(size_t n, const char *format, ...) { return nullptr; } +static const char characters[] = { + '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', +}; + void swoole_random_string(char *buf, size_t size) { - static char characters[] = { - '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - }; size_t i = 0; for (; i < size; i++) { buf[i] = characters[swoole_rand(0, sizeof(characters) - 1)]; @@ -766,6 +813,13 @@ void swoole_random_string(char *buf, size_t size) { buf[i] = '\0'; } +void swoole_random_string(std::string &str, size_t size) { + size_t i = 0; + for (; i < size; i++) { + str.append(1, characters[swoole_rand(0, sizeof(characters) - 1)]); + } +} + size_t swoole_random_bytes(char *buf, size_t size) { size_t read_bytes = 0; ssize_t n; @@ -840,7 +894,18 @@ static void swoole_fatal_error_impl(int code, const char *format, ...) { retval += sw_vsnprintf(sw_error + retval, SW_ERROR_MSG_SIZE - retval, format, args); va_end(args); sw_logger()->put(SW_LOG_ERROR, sw_error, retval); - exit(1); + swoole_exit(1); +} + +void swoole_exit(int __status) { +#ifdef SW_THREAD + /** + * If multiple threads call exit simultaneously, it can result in a crash. + * Implementing locking mechanisms can prevent concurrent calls to exit. + */ + std::unique_lock _lock(sw_thread_lock); +#endif + exit(__status); } namespace swoole { @@ -851,7 +916,7 @@ size_t DataHead::dump(char *_buf, size_t _len) { "DataHead[%p]\n" "{\n" " long fd = %ld;\n" - " uint64_t msg_id = %lu;\n" + " uint64_t msg_id = %" PRIu64 ";\n" " uint32_t len = %d;\n" " int16_t reactor_id = %d;\n" " uint8_t type = %d;\n" @@ -903,6 +968,9 @@ int hook_add(void **hooks, int type, const Callback &func, int push_back) { } void hook_call(void **hooks, int type, void *arg) { + if (hooks[type] == nullptr) { + return; + } std::list *l = reinterpret_cast *>(hooks[type]); for (auto i = l->begin(); i != l->end(); i++) { (*i)(arg); @@ -913,18 +981,13 @@ void hook_call(void **hooks, int type, void *arg) { * return the first file of the intersection, in order of vec1 */ std::string intersection(std::vector &vec1, std::set &vec2) { - std::string result = ""; - - std::find_if(vec1.begin(), vec1.end(), [&](std::string &str) -> bool { - auto iter = std::find(vec2.begin(), vec2.end(), str); - if (iter != vec2.end()) { - result = *iter; - return true; + for (const auto &vec1_item : vec1) { + if (vec2.find(vec1_item) != vec2.end()) { + return vec1_item; } - return false; - }); + } - return result; + return ""; } double microtime(void) { diff --git a/src/protocol/base64.cc b/src/core/base64.cc similarity index 98% rename from src/protocol/base64.cc rename to src/core/base64.cc index 7ca13c3cf31..0c430bfbf24 100644 --- a/src/protocol/base64.cc +++ b/src/core/base64.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -131,4 +131,4 @@ size_t base64_decode(const char *in, size_t inlen, char *out) { return j; } -} +} // namespace swoole diff --git a/src/core/channel.cc b/src/core/channel.cc index 02f0da317d4..2ce833790ca 100644 --- a/src/core/channel.cc +++ b/src/core/channel.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/crc32.cc b/src/core/crc32.cc index 8b581ff01de..aabf6f784c8 100644 --- a/src/core/crc32.cc +++ b/src/core/crc32.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/error.cc b/src/core/error.cc index 68419450ce2..4fdc154ab28 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -10,12 +10,16 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" +#include + +static std::unordered_set ignored_errors; + namespace swoole { Exception::Exception(int code) throw() : code(code) { msg = swoole_strerror(code); @@ -27,8 +31,7 @@ const char *swoole_strerror(int code) { return strerror(code); } /* swstrerror {{{*/ - switch(code) - { + switch (code) { case SW_ERROR_MALLOC_FAIL: return "Malloc fail"; case SW_ERROR_SYSTEM_CALL_FAIL: @@ -47,6 +50,16 @@ const char *swoole_strerror(int code) { return "Protocol error"; case SW_ERROR_WRONG_OPERATION: return "Wrong operation"; + case SW_ERROR_PHP_RUNTIME_NOTICE: + return "PHP runtime notice"; + case SW_ERROR_FOR_TEST: + return "For test"; + case SW_ERROR_NO_PAYLOAD: + return "No payload"; + case SW_ERROR_UNDEFINED_BEHAVIOR: + return "Undefined behavior"; + case SW_ERROR_NOT_THREAD_SAFETY: + return "Not thread safety"; case SW_ERROR_FILE_NOT_EXIST: return "File not exist"; case SW_ERROR_FILE_TOO_LARGE: @@ -67,6 +80,8 @@ const char *swoole_strerror(int code) { return "Bad ipv6 address"; case SW_ERROR_UNREGISTERED_SIGNAL: return "Unregistered signal"; + case SW_ERROR_BAD_HOST_ADDR: + return "Bad host addr"; case SW_ERROR_EVENT_SOCKET_REMOVED: return "Event socket removed"; case SW_ERROR_SESSION_CLOSED_BY_SERVER: @@ -111,6 +126,8 @@ const char *swoole_strerror(int code) { return "Package length not found"; case SW_ERROR_DATA_LENGTH_TOO_LARGE: return "Data length too large"; + case SW_ERROR_PACKAGE_MALFORMED_DATA: + return "Package malformed data"; case SW_ERROR_TASK_PACKAGE_TOO_BIG: return "Task package too big"; case SW_ERROR_TASK_DISPATCH_FAIL: @@ -125,6 +142,8 @@ const char *swoole_strerror(int code) { return "Http2 stream not found"; case SW_ERROR_HTTP2_STREAM_IGNORE: return "Http2 stream ignore"; + case SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED: + return "Http2 send control frame failed"; case SW_ERROR_AIO_BAD_REQUEST: return "Aio bad request"; case SW_ERROR_AIO_CANCELED: @@ -155,6 +174,12 @@ const char *swoole_strerror(int code) { return "Http proxy handshake failed"; case SW_ERROR_HTTP_PROXY_BAD_RESPONSE: return "Http proxy bad response"; + case SW_ERROR_HTTP_CONFLICT_HEADER: + return "Http conflict header"; + case SW_ERROR_HTTP_CONTEXT_UNAVAILABLE: + return "Http context unavailable"; + case SW_ERROR_HTTP_COOKIE_UNAVAILABLE: + return "Http cookie unavailable"; case SW_ERROR_WEBSOCKET_BAD_CLIENT: return "Websocket bad client"; case SW_ERROR_WEBSOCKET_BAD_OPCODE: @@ -165,6 +190,10 @@ const char *swoole_strerror(int code) { return "Websocket handshake failed"; case SW_ERROR_WEBSOCKET_PACK_FAILED: return "Websocket pack failed"; + case SW_ERROR_WEBSOCKET_UNPACK_FAILED: + return "Websocket unpack failed"; + case SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET: + return "Websocket incomplete packet"; case SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT: return "Server must created before client"; case SW_ERROR_SERVER_TOO_MANY_SOCKET: @@ -189,6 +218,14 @@ const char *swoole_strerror(int code) { return "Server connect fail"; case SW_ERROR_SERVER_INVALID_COMMAND: return "Server invalid command"; + case SW_ERROR_SERVER_IS_NOT_REGULAR_FILE: + return "Server is not regular file"; + case SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT: + return "Server send to woker timeout"; + case SW_ERROR_SERVER_INVALID_CALLBACK: + return "Server invalid callback"; + case SW_ERROR_SERVER_UNRELATED_THREAD: + return "Server unrelated thread"; case SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT: return "Server worker exit timeout"; case SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA: @@ -231,6 +268,8 @@ const char *swoole_strerror(int code) { return "Coroutine canceled"; case SW_ERROR_CO_TIMEDOUT: return "Coroutine timedout"; + case SW_ERROR_CO_SOCKET_CLOSE_WAIT: + return "Coroutine socket close wait"; default: static char buffer[32]; #ifndef __MACH__ @@ -240,9 +279,25 @@ const char *swoole_strerror(int code) { #endif return buffer; } -/*}}}*/ + /*}}}*/ } void swoole_throw_error(int code) { throw swoole::Exception(code); } + +void swoole_ignore_error(int code) { + ignored_errors.insert(code); +} + +bool swoole_is_ignored_error(int code) { + return ignored_errors.find(code) != ignored_errors.end(); +} + +void swoole_clear_last_error_msg(void) { + sw_error[0] = '\0'; +} + +const char *swoole_get_last_error_msg(void) { + return sw_error; +} diff --git a/src/core/heap.cc b/src/core/heap.cc index 4f6054b8a43..6ffbeb4877c 100644 --- a/src/core/heap.cc +++ b/src/core/heap.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/log.cc b/src/core/log.cc index 2993398c5d9..987c9725cc7 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -328,21 +328,11 @@ void Logger::put(int level, const char *content, size_t length) { content); if (opened && flock(log_fd, LOCK_EX) == -1) { - printf("flock(%d, LOCK_EX) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno); - goto _print; - } - if (write(log_fd, log_str, n) < 0) { - _print: - printf("write(log_fd=%d, size=%d) failed. Error: %s[%d].\nMessage: %.*s\n", - log_fd, - n, - strerror(errno), - errno, - n, - log_str); + return; } + write(log_fd, log_str, n); if (opened && flock(log_fd, LOCK_UN) == -1) { - printf("flock(%d, LOCK_UN) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno); + return; } if (display_backtrace_) { swoole_print_backtrace(); diff --git a/src/core/string.cc b/src/core/string.cc index 2569a65661d..454c170d134 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -60,8 +60,12 @@ void String::reduce(off_t _offset) { memmove(str, str + _offset, length); } -void String::print() { - printf("String[length=%zu,size=%zu,offset=%jd]=%.*s\n", length, size, (intmax_t) offset, (int) length, str); +void String::print(bool print_value) { + if (print_value) { + printf("String[length=%zu,size=%zu,offset=%jd]=%.*s\n", length, size, (intmax_t) offset, (int) length, str); + } else { + printf("String[length=%zu,size=%zu,offset=%jd]=%p\n", length, size, (intmax_t) offset, str); + } } int String::append(int value) { @@ -101,7 +105,7 @@ int String::append_random_bytes(size_t _length, bool base64) { } if (new_size > size) { - if (!reserve(swoole_size_align(new_size * 2, SwooleG.pagesize))) { + if (!reserve(swoole_size_align(new_size * 2, swoole_pagesize()))) { return SW_ERR; } } @@ -141,6 +145,15 @@ bool String::reserve(size_t new_size) { return true; } +char *String::release() { + char *tmp = str; + str = nullptr; + size = 0; + length = 0; + offset = 0; + return tmp; +} + bool String::repeat(const char *data, size_t len, size_t n) { if (n <= 0) { return false; @@ -176,11 +189,16 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri off_t _offset = offset; size_t ret; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%ld", count, length, size, offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, + "#[0] count=%d, length=%ld, size=%ld, offset=%jd", + count, + length, + size, + (intmax_t) offset); while (delimiter_addr) { size_t _length = delimiter_addr - start_addr + delimiter_length; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%lu", count, _length + offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%zu", count, (size_t) (_length + offset)); if (handler((char *) start_addr - _offset, _length + _offset) == false) { return -1; } @@ -203,9 +221,11 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri ret = start_addr - str - _offset; if (ret > 0 && ret < length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, length - offset); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); } else if (ret >= length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%ld, offset=%ld", length, size, offset); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); } return ret; diff --git a/src/core/timer.cc b/src/core/timer.cc index 585cb0182e5..21798968cba 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,7 +29,7 @@ static double orwl_timebase = 0.0; static uint64_t orwl_timestart = 0; -int swoole_clock_gettime(int which_clock, struct timespec *t) { +int swoole_clock_realtime(struct timespec *t) { // be more careful in a multithreaded environement if (!orwl_timestart) { mach_timebase_info_data_t tb = {0}; @@ -60,13 +60,21 @@ bool Timer::init() { return false; } if (SwooleTG.reactor) { - return init_reactor(SwooleTG.reactor); + return init_with_reactor(SwooleTG.reactor); + } else if (SwooleTG.timer_scheduler) { + return init_with_user_scheduler(SwooleTG.timer_scheduler); } else { - return init_system_timer(); + return init_with_system_timer(); } } -bool Timer::init_reactor(Reactor *reactor) { +bool Timer::init_with_user_scheduler(const TimerScheduler &scheduler) { + set = [&scheduler](Timer *timer, long exec_msec) -> int { return scheduler(timer, exec_msec); }; + close = [&scheduler](Timer *timer) { scheduler(timer, -1); }; + return true; +} + +bool Timer::init_with_reactor(Reactor *reactor) { reactor_ = reactor; set = [](Timer *timer, long exec_msec) -> int { timer->reactor_->timeout_msec = exec_msec; @@ -89,7 +97,7 @@ bool Timer::init_reactor(Reactor *reactor) { } void Timer::reinit(Reactor *reactor) { - init_reactor(reactor); + init_with_reactor(reactor); reactor->timeout_msec = next_msec_; } @@ -226,6 +234,7 @@ int Timer::select() { heap.pop(); map.erase(tnode->id); delete tnode; + tnode = nullptr; } if (!tnode || !tmp) { @@ -243,8 +252,8 @@ int Timer::select() { return SW_OK; } -#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC) int Timer::now(struct timeval *time) { +#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC) struct timespec _now; if (clock_gettime(CLOCK_MONOTONIC, &_now) < 0) { swoole_sys_warning("clock_gettime(CLOCK_MONOTONIC) failed"); @@ -253,12 +262,12 @@ int Timer::now(struct timeval *time) { time->tv_sec = _now.tv_sec; time->tv_usec = _now.tv_nsec / 1000; #else -if (gettimeofday(time, nullptr) < 0) { - swoole_sys_warning("gettimeofday() failed"); - return SW_ERR; -} + if (gettimeofday(time, nullptr) < 0) { + swoole_sys_warning("gettimeofday() failed"); + return SW_ERR; + } #endif return SW_OK; -} // namespace swoole +} }; // namespace swoole diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index bf9d690a2c9..cb1b2ee47f0 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,17 +19,17 @@ namespace swoole { -Coroutine *Coroutine::current = nullptr; -long Coroutine::last_cid = 0; -std::unordered_map Coroutine::coroutines; -uint64_t Coroutine::peak_num = 0; -bool Coroutine::activated = false; +SW_THREAD_LOCAL Coroutine *Coroutine::current = nullptr; +SW_THREAD_LOCAL long Coroutine::last_cid = 0; +SW_THREAD_LOCAL std::unordered_map Coroutine::coroutines; +SW_THREAD_LOCAL uint64_t Coroutine::peak_num = 0; +SW_THREAD_LOCAL bool Coroutine::activated = false; -size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; -Coroutine::SwapCallback Coroutine::on_yield = nullptr; -Coroutine::SwapCallback Coroutine::on_resume = nullptr; -Coroutine::SwapCallback Coroutine::on_close = nullptr; -Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; +SW_THREAD_LOCAL size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_yield = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_resume = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_close = nullptr; +SW_THREAD_LOCAL Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; #ifdef SW_USE_THREAD_CONTEXT namespace coroutine { @@ -43,6 +43,7 @@ void Coroutine::activate() { coroutine::thread_context_init(); #endif activated = true; + on_bailout = nullptr; } void Coroutine::deactivate() { @@ -50,6 +51,12 @@ void Coroutine::deactivate() { coroutine::thread_context_clean(); #endif activated = false; + on_bailout = [](){ + // The coroutine scheduler has been destroyed, + // Can not resume any coroutine + // Expect that never here + swoole_error("have been bailout, can not resume any coroutine"); + }; } void Coroutine::yield() { @@ -60,6 +67,8 @@ void Coroutine::yield() { on_yield(task); } current = origin; + + CALC_EXECUTE_USEC(this, current); ctx.swap_out(); } @@ -77,7 +86,7 @@ bool Coroutine::yield_ex(double timeout) { }; if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, timer_callback, nullptr); + timer = swoole_timer_add(timeout, false, timer_callback, nullptr); } CancelFunc cancel_fn = [](Coroutine *co) { @@ -112,6 +121,8 @@ void Coroutine::resume() { } origin = current; current = this; + + CALC_EXECUTE_USEC(origin, this); ctx.swap_in(); check_end(); } @@ -182,15 +193,15 @@ void Coroutine::bailout(BailoutCallback func) { Coroutine *co = current; if (!co) { // marks that it can no longer resume any coroutine - on_bailout = (BailoutCallback) -1; + static BailoutCallback fn = []() { + // expect that never here + swoole_error("have been bailout, can not resume any coroutine"); + }; + on_bailout = fn; return; } if (!func) { - swoole_error("bailout without bailout function"); - } - if (!co->task) { - // TODO: decoupling - exit(255); + swoole_error("bailout without callback function"); } on_bailout = func; // find the coroutine which is closest to the main @@ -200,7 +211,7 @@ void Coroutine::bailout(BailoutCallback func) { // it will jump to main context directly (it also breaks contexts) co->yield(); // expect that never here - exit(1); + exit(SW_CORO_BAILOUT_EXIT_CODE); } namespace coroutine { diff --git a/src/coroutine/channel.cc b/src/coroutine/channel.cc index 2526257ce81..f0806728e78 100644 --- a/src/coroutine/channel.cc +++ b/src/coroutine/channel.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -63,11 +63,10 @@ void *Channel::pop(double timeout) { msg.error = false; msg.timer = nullptr; if (timeout > 0) { - long msec = (long) (timeout * 1000); msg.chan = this; msg.type = CONSUMER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, false, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(CONSUMER); @@ -114,11 +113,10 @@ bool Channel::push(void *data, double timeout) { msg.error = false; msg.timer = nullptr; if (timeout > 0) { - long msec = (long) (timeout * 1000); msg.chan = this; msg.type = PRODUCER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, false, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(PRODUCER); diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index faa830794c7..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -31,8 +31,8 @@ namespace swoole { namespace coroutine { -Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) - : fn_(fn), stack_size_(stack_size), private_data_(private_data) { +Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) + : fn_(std::move(fn)), stack_size_(stack_size), private_data_(private_data) { end_ = false; #ifdef SW_CONTEXT_PROTECT_STACK_PAGE @@ -57,7 +57,7 @@ Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) valgrind_stack_id = VALGRIND_STACK_REGISTER(sp, stack_); #endif -#if USE_UCONTEXT +#ifdef USE_UCONTEXT if (-1 == getcontext(&ctx_)) { swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED); sw_free(stack_); @@ -68,7 +68,7 @@ Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) ctx_.uc_link = nullptr; makecontext(&ctx_, (void (*)(void)) & context_func, 1, this); #else - ctx_ = make_fcontext(sp, stack_size_, (void (*)(intptr_t)) & context_func); + ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(transfer_t)) & context_func); swap_ctx_ = nullptr; #endif @@ -120,25 +120,32 @@ ssize_t Context::get_stack_usage() { #endif bool Context::swap_in() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&swap_ctx_, &ctx_); #else - jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this); + ctx_ = transfer_data.fctx; return true; #endif } bool Context::swap_out() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&ctx_, &swap_ctx_); #else - jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(swap_ctx_, (void *) this); + swap_ctx_ = transfer_data.fctx; return true; #endif } -void Context::context_func(void *arg) { - Context *_this = (Context *) arg; +void Context::context_func(coroutine_transfer_t arg) { +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) + auto *_this = (Context *) arg; +#else + auto *_this = (Context *) arg.data; + _this->swap_ctx_ = arg.fctx; +#endif _this->fn_(_this->private_data_); _this->end_ = true; _this->swap_out(); diff --git a/src/coroutine/file_lock.cc b/src/coroutine/file_lock.cc index 02a69fdebee..af040bbdc12 100644 --- a/src/coroutine/file_lock.cc +++ b/src/coroutine/file_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 2c17c8de83c..1649d4302a5 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,21 +26,30 @@ #include "swoole_coroutine_socket.h" #include "swoole_coroutine_system.h" +#include "swoole_iouring.h" using swoole::AsyncEvent; using swoole::Coroutine; using swoole::async::dispatch; +using swoole::coroutine::async; +using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; +using swoole::coroutine::translate_events_from_poll; +using swoole::coroutine::translate_events_to_poll; -static std::unordered_map socket_map; +#ifdef SW_USE_IOURING +using swoole::Iouring; +#endif + +static std::unordered_map> socket_map; static std::mutex socket_map_lock; static sw_inline bool is_no_coro() { return SwooleTG.reactor == nullptr || !Coroutine::get_current(); } -static sw_inline Socket *get_socket(int sockfd) { +static sw_inline std::shared_ptr get_socket(int sockfd) { std::unique_lock _lock(socket_map_lock); auto socket_iterator = socket_map.find(sockfd); if (socket_iterator == socket_map.end()) { @@ -49,14 +58,14 @@ static sw_inline Socket *get_socket(int sockfd) { return socket_iterator->second; } -static sw_inline Socket *get_socket_ex(int sockfd) { +static sw_inline std::shared_ptr get_socket_ex(int sockfd) { if (sw_unlikely(is_no_coro())) { return nullptr; } return get_socket(sockfd); } -Socket *swoole_coroutine_get_socket_object(int sockfd) { +std::shared_ptr swoole_coroutine_get_socket_object(int sockfd) { return get_socket(sockfd); } @@ -66,10 +75,10 @@ int swoole_coroutine_socket(int domain, int type, int protocol) { if (sw_unlikely(is_no_coro())) { return ::socket(domain, type, protocol); } - Socket *socket = new Socket(domain, type, protocol); + auto socket = std::make_shared(domain, type, protocol); int fd = socket->get_fd(); if (sw_unlikely(fd < 0)) { - delete socket; + return -1; } else { std::unique_lock _lock(socket_map_lock); socket_map[fd] = socket; @@ -78,32 +87,32 @@ int swoole_coroutine_socket(int domain, int type, int protocol) { } ssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::send(sockfd, buf, len, flags); } return socket->send(buf, len); } ssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::sendmsg(sockfd, msg, flags); } return socket->sendmsg(msg, flags); } ssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::recvmsg(sockfd, msg, flags); } return socket->recvmsg(msg, flags); } ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::recv(sockfd, buf, len, flags); } if (flags & MSG_PEEK) { @@ -114,30 +123,34 @@ ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) { } int swoole_coroutine_close(int sockfd) { - Socket *socket = get_socket(sockfd); - if (socket == NULL) { + auto socket = get_socket(sockfd); + if (socket == nullptr) { return ::close(sockfd); } if (socket->close()) { - delete socket; std::unique_lock _lock(socket_map_lock); socket_map.erase(sockfd); + return 0; } - return 0; + return -1; } int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::connect(sockfd, addr, addrlen); } return socket->connect(addr, addrlen) ? 0 : -1; } -#if 1 -int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - Socket *socket; - if (sw_unlikely(nfds != 1 || timeout == 0 || (socket = get_socket_ex(fds[0].fd)) == NULL)) { +int swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout) { + if (nfds != 1) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + swoole_warning("fake poll() implementation, only supports one socket"); + return -1; + } + auto socket = get_socket_ex(fds[0].fd); + if (sw_unlikely(timeout == 0 || socket == nullptr)) { return poll(fds, nfds, timeout); } socket->set_timeout((double) timeout / 1000); @@ -149,26 +162,26 @@ int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { } return 1; } -#else + int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - if (sw_unlikely(is_no_coro() || nfds != 1 || timeout == 0)) { + if (sw_unlikely(is_no_coro() || timeout == 0)) { return poll(fds, nfds, timeout); } - std::unordered_map _fds; - for (int i = 0; i < nfds; i++) { - _fds.emplace(std::make_pair(fds[i].fd, swoole::socket_poll_fd(fds[i].events, &fds[i]))); + std::unordered_map _fds; + for (nfds_t i = 0; i < nfds; i++) { + _fds.emplace(std::make_pair(fds[i].fd, PollSocket(translate_events_from_poll(fds[i].events), &fds[i]))); } if (!System::socket_poll(_fds, (double) timeout / 1000)) { return -1; } - int retval; + int retval = 0; for (auto &i : _fds) { int revents = i.second.revents; struct pollfd *_fd = (struct pollfd *) i.second.ptr; - _fd->revents = revents; + _fd->revents = translate_events_to_poll(revents); if (revents > 0) { retval++; } @@ -176,7 +189,6 @@ int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { return retval; } -#endif int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { if (sw_unlikely(is_no_coro())) { @@ -184,7 +196,17 @@ int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { } int ret = -1; - swoole::coroutine::async([&]() { ret = open(pathname, flags, mode); }); + async([&]() { ret = open(pathname, flags, mode); }); + return ret; +} + +int swoole_coroutine_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } + + int ret = -1; + async([&]() { ret = close(fd); }); return ret; } @@ -192,14 +214,28 @@ int swoole_coroutine_socket_create(int fd) { if (sw_unlikely(is_no_coro())) { return -1; } - Socket *socket = new Socket(fd, SW_SOCK_RAW); + auto socket = std::make_shared(fd, SW_SOCK_RAW); int _fd = socket->get_fd(); if (sw_unlikely(_fd < 0)) { - delete socket; - } else { - std::unique_lock _lock(socket_map_lock); - socket_map[fd] = socket; + return -1; + } + socket->get_socket()->set_nonblock(); + std::unique_lock _lock(socket_map_lock); + socket_map[fd] = socket; + return 0; +} + +int swoole_coroutine_socket_unwrap(int fd) { + if (sw_unlikely(is_no_coro())) { + return -1; + } + auto socket = get_socket(fd); + if (socket == nullptr) { + return -1; } + std::unique_lock _lock(socket_map_lock); + socket->move_fd(); + socket_map.erase(fd); return 0; } @@ -212,13 +248,13 @@ ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { return read(sockfd, buf, count); } - Socket *socket = get_socket(sockfd); - if (socket) { + auto socket = get_socket(sockfd); + if (socket != nullptr) { return socket->read(buf, count); } ssize_t ret = -1; - swoole::coroutine::async([&]() { ret = read(sockfd, buf, count); }); + async([&]() { ret = read(sockfd, buf, count); }); return ret; } @@ -227,13 +263,13 @@ ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) { return write(sockfd, buf, count); } - Socket *socket = get_socket(sockfd); - if (socket) { + auto socket = get_socket(sockfd); + if (socket != nullptr) { return socket->write(buf, count); } ssize_t ret = -1; - swoole::coroutine::async([&]() { ret = write(sockfd, buf, count); }); + async([&]() { ret = write(sockfd, buf, count); }); return ret; } @@ -243,7 +279,7 @@ off_t swoole_coroutine_lseek(int fd, off_t offset, int whence) { } off_t retval = -1; - swoole::coroutine::async([&]() { retval = lseek(fd, offset, whence); }); + async([&]() { retval = lseek(fd, offset, whence); }); return retval; } @@ -253,7 +289,7 @@ int swoole_coroutine_fstat(int fd, struct stat *statbuf) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fstat(fd, statbuf); }); + async([&]() { retval = fstat(fd, statbuf); }); return retval; } @@ -263,7 +299,7 @@ int swoole_coroutine_readlink(const char *pathname, char *buf, size_t len) { } int retval = -1; - swoole::coroutine::async([&]() { retval = readlink(pathname, buf, len); }); + async([&]() { retval = readlink(pathname, buf, len); }); return retval; } @@ -273,7 +309,7 @@ int swoole_coroutine_unlink(const char *pathname) { } int retval = -1; - swoole::coroutine::async([&]() { retval = unlink(pathname); }); + async([&]() { retval = unlink(pathname); }); return retval; } @@ -283,7 +319,27 @@ int swoole_coroutine_statvfs(const char *path, struct statvfs *buf) { } int retval = -1; - swoole::coroutine::async([&]() { retval = statvfs(path, buf); }); + async([&]() { retval = statvfs(path, buf); }); + return retval; +} + +int swoole_coroutine_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); + } + + int retval = -1; + async([&]() { retval = stat(path, statbuf); }); + return retval; +} + +int swoole_coroutine_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); + } + + int retval = -1; + async([&]() { retval = lstat(path, statbuf); }); return retval; } @@ -293,7 +349,7 @@ int swoole_coroutine_mkdir(const char *pathname, mode_t mode) { } int retval = -1; - swoole::coroutine::async([&]() { retval = mkdir(pathname, mode); }); + async([&]() { retval = mkdir(pathname, mode); }); return retval; } @@ -303,7 +359,7 @@ int swoole_coroutine_rmdir(const char *pathname) { } int retval = -1; - swoole::coroutine::async([&]() { retval = rmdir(pathname); }); + async([&]() { retval = rmdir(pathname); }); return retval; } @@ -313,7 +369,7 @@ int swoole_coroutine_rename(const char *oldpath, const char *newpath) { } int retval = -1; - swoole::coroutine::async([&]() { retval = rename(oldpath, newpath); }); + async([&]() { retval = rename(oldpath, newpath); }); return retval; } @@ -323,7 +379,7 @@ int swoole_coroutine_access(const char *pathname, int mode) { } int retval = -1; - swoole::coroutine::async([&]() { retval = access(pathname, mode); }); + async([&]() { retval = access(pathname, mode); }); return retval; } @@ -333,7 +389,7 @@ FILE *swoole_coroutine_fopen(const char *pathname, const char *mode) { } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = fopen(pathname, mode); }); + async([&]() { retval = fopen(pathname, mode); }); return retval; } @@ -343,7 +399,7 @@ FILE *swoole_coroutine_fdopen(int fd, const char *mode) { } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = fdopen(fd, mode); }); + async([&]() { retval = fdopen(fd, mode); }); return retval; } @@ -353,7 +409,7 @@ FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *str } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = freopen(pathname, mode, stream); }); + async([&]() { retval = freopen(pathname, mode, stream); }); return retval; } @@ -363,7 +419,7 @@ size_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream } size_t retval = 0; - swoole::coroutine::async([&]() { retval = fread(ptr, size, nmemb, stream); }); + async([&]() { retval = fread(ptr, size, nmemb, stream); }); return retval; } @@ -373,7 +429,7 @@ size_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE } size_t retval = 0; - swoole::coroutine::async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); + async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); return retval; } @@ -383,8 +439,8 @@ char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { } char *retval = nullptr; - swoole::coroutine::async([&]() { retval = fgets(s, size, stream); }); - return retval; + async([&]() { retval = fgets(s, size, stream); }); + return retval; } int swoole_coroutine_fputs(const char *s, FILE *stream) { @@ -393,8 +449,8 @@ int swoole_coroutine_fputs(const char *s, FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fputs(s, stream); }); - return retval; + async([&]() { retval = fputs(s, stream); }); + return retval; } int swoole_coroutine_feof(FILE *stream) { @@ -403,7 +459,7 @@ int swoole_coroutine_feof(FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = feof(stream); }); + async([&]() { retval = feof(stream); }); return retval; } @@ -413,7 +469,7 @@ int swoole_coroutine_fclose(FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fclose(stream); }); + async([&]() { retval = fclose(stream); }); return retval; } @@ -423,7 +479,7 @@ int swoole_coroutine_flock(int fd, int operation) { } int retval = -1; - swoole::coroutine::async([&]() { retval = flock(fd, operation); }); + async([&]() { retval = flock(fd, operation); }); return retval; } @@ -433,7 +489,7 @@ DIR *swoole_coroutine_opendir(const char *name) { } DIR *retval = nullptr; - swoole::coroutine::async([&]() { retval = opendir(name); }); + async([&]() { retval = opendir(name); }); return retval; } @@ -444,9 +500,7 @@ struct dirent *swoole_coroutine_readdir(DIR *dirp) { struct dirent *retval; - swoole::coroutine::async([&retval, dirp]() { - retval = readdir(dirp); - }); + async([&retval, dirp]() { retval = readdir(dirp); }); return retval; } @@ -457,7 +511,7 @@ int swoole_coroutine_closedir(DIR *dirp) { } int retval = -1; - swoole::coroutine::async([&]() { retval = closedir(dirp); }); + async([&]() { retval = closedir(dirp); }); return retval; } @@ -470,8 +524,8 @@ void swoole_coroutine_usleep(int usec) { } int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } @@ -488,8 +542,8 @@ int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { } int swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } @@ -498,8 +552,8 @@ int swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) { } int swoole_coroutine_socket_wait_event(int sockfd, int event, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } @@ -515,14 +569,14 @@ int swoole_coroutine_getaddrinfo(const char *name, const struct addrinfo *req, struct addrinfo **pai) { int retval = -1; - swoole::coroutine::async([&]() { retval = getaddrinfo(name, service, req, pai); }); + async([&]() { retval = getaddrinfo(name, service, req, pai); }); return retval; } struct hostent *swoole_coroutine_gethostbyname(const char *name) { struct hostent *retval = nullptr; - int _tmp_h_errno; - swoole::coroutine::async([&]() { + int _tmp_h_errno = 0; + async([&]() { retval = gethostbyname(name); _tmp_h_errno = h_errno; }); @@ -530,4 +584,128 @@ struct hostent *swoole_coroutine_gethostbyname(const char *name) { return retval; } +int swoole_coroutine_fsync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fsync(fd); + } + + int retval = -1; + async([&]() { retval = fsync(fd); }); + return retval; +} + +int swoole_coroutine_fdatasync(int fd) { + if (sw_unlikely(is_no_coro())) { +#ifndef HAVE_FDATASYNC + return fsync(fd); +#else + return fdatasync(fd); +#endif + } + + int retval = -1; +#ifndef HAVE_FDATASYNC + async([&]() { retval = fsync(fd); }); +#else + async([&]() { retval = fdatasync(fd); }); +#endif + return retval; +} + +#ifdef SW_USE_IOURING +int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return open(pathname, flags, mode); + } + return Iouring::open(pathname, flags, mode); +} + +int swoole_coroutine_iouring_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } + return Iouring::close(fd); +} + +ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t size) { + if (sw_unlikely(is_no_coro())) { + return read(sockfd, buf, size); + } + return Iouring::read(sockfd, buf, size); +} + +ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t size) { + if (sw_unlikely(is_no_coro())) { + return write(sockfd, buf, size); + } + return Iouring::write(sockfd, buf, size); +} + +int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath) { + if (sw_unlikely(is_no_coro())) { + return rename(oldpath, newpath); + } + return Iouring::rename(oldpath, newpath); +} + +int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return mkdir(pathname, mode); + } + return Iouring::mkdir(pathname, mode); +} + +int swoole_coroutine_iouring_unlink(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return unlink(pathname); + } + return Iouring::unlink(pathname); +} + +#ifdef HAVE_IOURING_STATX +int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return fstat(fd, statbuf); + } + return Iouring::fstat(fd, statbuf); +} + +int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); + } + return Iouring::stat(path, statbuf); +} + +int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); + } + // Iouring cannot distinguish between lstat and stat; these two operations are the same + return Iouring::stat(path, statbuf); +} +#endif + +int swoole_coroutine_iouring_rmdir(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return rmdir(pathname); + } + return Iouring::rmdir(pathname); +} + +int swoole_coroutine_iouring_fsync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fsync(fd); + } + return Iouring::fsync(fd); +} + +int swoole_coroutine_iouring_fdatasync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fdatasync(fd); + } + return Iouring::fdatasync(fd); +} +#endif + SW_EXTERN_C_END diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc new file mode 100644 index 00000000000..690704d63d7 --- /dev/null +++ b/src/coroutine/iouring.cc @@ -0,0 +1,527 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author NathanFreeman | + +----------------------------------------------------------------------+ +*/ + +#include "swoole_iouring.h" + +#ifdef SW_USE_IOURING +using swoole::Coroutine; + +namespace swoole { +//------------------------------------------------------------------------------- +enum IouringOpcode { + SW_IORING_OP_OPENAT = IORING_OP_OPENAT, + SW_IORING_OP_CLOSE = IORING_OP_CLOSE, + SW_IORING_OP_STATX = IORING_OP_STATX, + SW_IORING_OP_READ = IORING_OP_READ, + SW_IORING_OP_WRITE = IORING_OP_WRITE, + SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT, + SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT, +#ifdef HAVE_IOURING_FUTEX + SW_IORING_OP_FUTEX_WAIT = IORING_OP_FUTEX_WAIT, + SW_IORING_OP_FUTEX_WAKE = IORING_OP_FUTEX_WAKE, +#endif + + SW_IORING_OP_FSTAT = 1000, + SW_IORING_OP_LSTAT = 1001, + SW_IORING_OP_UNLINK_FILE = 1002, + SW_IORING_OP_UNLINK_DIR = 1003, + SW_IORING_OP_FSYNC = 1004, + SW_IORING_OP_FDATASYNC = 1005, +}; + +struct IouringEvent { + IouringOpcode opcode; + Coroutine *coroutine; + int fd; + int flags; + union { + mode_t mode; + size_t size; + }; + ssize_t result; + const char *pathname; + union { + void *rbuf; + const void *wbuf; + struct statx *statxbuf; + const char *pathname2; +#ifdef HAVE_IOURING_FUTEX + uint32_t *futex; +#endif + }; +}; + +Iouring::Iouring(Reactor *_reactor) { + if (!SwooleTG.reactor) { + swoole_warning("no event loop, cannot initialized"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + } + + reactor = _reactor; + if (SwooleG.iouring_entries > 0) { + uint32_t i = 6; + while ((1U << i) < SwooleG.iouring_entries) { + i++; + } + entries = 1 << i; + } + + int ret = + io_uring_queue_init(entries, &ring, (SwooleG.iouring_flag == IORING_SETUP_SQPOLL ? IORING_SETUP_SQPOLL : 0)); + if (ret < 0) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "Create io_uring failed, the error code is %d", -ret); + return; + } + + if (SwooleG.iouring_workers > 0) { + uint32_t workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; + ret = io_uring_register_iowq_max_workers(&ring, workers); + + if (ret < 0) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SYSTEM_CALL_FAIL, + "Failed to increase io_uring async workers, the error code is %d", + -ret); + return; + } + } + + ring_socket = make_socket(ring.ring_fd, SW_FD_IOURING); + ring_socket->object = this; + + reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { + if (SwooleTG.iouring && SwooleTG.iouring->get_task_num() == 0 && SwooleTG.iouring->is_empty_waiting_tasks()) { + event_num--; + } + return true; + }); + + reactor->add_destroy_callback([](void *data) { + if (!SwooleTG.iouring) { + return; + } + delete SwooleTG.iouring; + SwooleTG.iouring = nullptr; + }); + + reactor->add(ring_socket, SW_EVENT_READ); +} + +Iouring::~Iouring() { + if (!ring_socket) { + return; + } + + if (!ring_socket->removed) { + reactor->del(ring_socket); + } + ring_socket->move_fd(); + ring_socket->free(); + ring_socket = nullptr; + + io_uring_queue_exit(&ring); +} + +bool Iouring::ready() { + return ring_socket && reactor->exists(ring_socket); +} + +bool Iouring::wakeup() { + IouringEvent *waiting_task = nullptr; + struct io_uring_cqe *cqes[SW_IOURING_CQES_SIZE]; + + while (true) { + auto count = io_uring_peek_batch_cqe(&ring, cqes, SW_IOURING_CQES_SIZE); + if (count == 0) { + return true; + } + + for (decltype(count) i = 0; i < count; i++) { + struct io_uring_cqe *cqe = cqes[i]; + IouringEvent *task = static_cast(io_uring_cqe_get_data(cqe)); + task_num--; + if (cqe->res < 0) { + errno = -(cqe->res); + /** + * If the error code is EAGAIN, it indicates that the resource is temporarily unavailable, + * but it can be retried. However, for the fairness of the tasks, this task should be placed + * at the end of the queue. + */ + if (cqe->res == -EAGAIN) { + io_uring_cq_advance(&ring, 1); + waiting_tasks.push(task); + continue; + } + } + + task->result = (cqe->res >= 0 ? cqe->res : -1); + io_uring_cq_advance(&ring, 1); + + task->coroutine->resume(); + + if (!is_empty_waiting_tasks()) { + waiting_task = waiting_tasks.front(); + waiting_tasks.pop(); + if (!dispatch(waiting_task)) { + waiting_task->coroutine->resume(); + } + } + } + } + + return true; +} + +static const char *get_opcode_name(IouringOpcode opcode) { + switch (opcode) { + case SW_IORING_OP_OPENAT: + return "OPENAT"; + case SW_IORING_OP_CLOSE: + return "CLOSE"; + case SW_IORING_OP_STATX: + return "STATX"; + case SW_IORING_OP_READ: + return "READ"; + case SW_IORING_OP_WRITE: + return "WRITE"; + case SW_IORING_OP_RENAMEAT: + return "RENAMEAT"; + case SW_IORING_OP_MKDIRAT: + return "MKDIRAT"; + case SW_IORING_OP_FSTAT: + return "FSTAT"; + case SW_IORING_OP_LSTAT: + return "LSTAT"; + case SW_IORING_OP_UNLINK_FILE: + return "UNLINK_FILE"; + case SW_IORING_OP_UNLINK_DIR: + return "UNLINK_DIR"; + case SW_IORING_OP_FSYNC: + return "FSYNC"; + case SW_IORING_OP_FDATASYNC: + return "FDATASYNC"; +#ifdef HAVE_IOURING_FUTEX + case SW_IORING_OP_FUTEX_WAIT: + return "FUTEX_WAIT"; + case SW_IORING_OP_FUTEX_WAKE: + return "FUTEX_WAKE"; +#endif + default: + return "unknown"; + } +} + +bool Iouring::submit(IouringEvent *event) { + swoole_trace("opcode=%s, fd=%d, path=%s", get_opcode_name(event->opcode), event->fd, event->pathname); + + int ret = io_uring_submit(&ring); + + if (ret < 0) { + if (-ret == EAGAIN) { + waiting_tasks.push(event); + return true; + } + swoole_set_last_error(-ret); + event->result = -1; + return false; + } + + task_num++; + return true; +} + +ssize_t Iouring::execute(IouringEvent *event) { + if (sw_unlikely(!SwooleTG.iouring)) { + auto iouring = new Iouring(SwooleTG.reactor); + if (!iouring->ready()) { + delete iouring; + return SW_ERR; + } + SwooleTG.iouring = iouring; + } + + if (!SwooleTG.iouring->dispatch(event)) { + return SW_ERR; + } + + // File system operations cannot be canceled, must wait to be completed. + event->coroutine->yield(); + + return event->result; +} + +bool Iouring::dispatch(IouringEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waiting_tasks.push(event); + return true; + } + + io_uring_sqe_set_data(sqe, (void *) event); + + switch (event->opcode) { + case SW_IORING_OP_OPENAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_OPENAT; + sqe->open_flags = event->flags | O_CLOEXEC; + break; + case SW_IORING_OP_READ: + case SW_IORING_OP_WRITE: + sqe->fd = event->fd; + sqe->addr = (uintptr_t) (event->opcode == SW_IORING_OP_READ ? event->rbuf : event->wbuf); + sqe->len = event->size; + sqe->off = -1; + sqe->opcode = event->opcode; + break; + case SW_IORING_OP_CLOSE: + sqe->fd = event->fd; + sqe->opcode = SW_IORING_OP_CLOSE; + break; + case SW_IORING_OP_FSTAT: + case SW_IORING_OP_LSTAT: + if (event->opcode == SW_IORING_OP_FSTAT) { + sqe->addr = (uintptr_t) ""; + sqe->fd = event->fd; + sqe->statx_flags |= AT_EMPTY_PATH; + } else { + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; + } + sqe->opcode = SW_IORING_OP_STATX; + sqe->off = (uintptr_t) event->statxbuf; + break; + case SW_IORING_OP_MKDIRAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_MKDIRAT; + break; + + case SW_IORING_OP_UNLINK_FILE: + case SW_IORING_OP_UNLINK_DIR: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->opcode = IORING_OP_UNLINKAT; + if (event->opcode == SW_IORING_OP_UNLINK_DIR) { + sqe->unlink_flags |= AT_REMOVEDIR; + } + break; + case SW_IORING_OP_RENAMEAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->addr2 = (uintptr_t) event->pathname2; + sqe->fd = AT_FDCWD; + sqe->len = AT_FDCWD; + sqe->opcode = SW_IORING_OP_RENAMEAT; + break; + case SW_IORING_OP_FSYNC: + case SW_IORING_OP_FDATASYNC: + sqe->fd = event->fd; + sqe->addr = (uintptr_t) nullptr; + sqe->opcode = IORING_OP_FSYNC; + sqe->len = 0; + sqe->off = 0; + sqe->fsync_flags = 0; + if (event->opcode == SW_IORING_OP_FDATASYNC) { + sqe->fsync_flags = IORING_FSYNC_DATASYNC; + } + break; +#ifdef HAVE_IOURING_FUTEX + case SW_IORING_OP_FUTEX_WAIT: + sqe->opcode = SW_IORING_OP_FUTEX_WAIT; + sqe->fd = FUTEX2_SIZE_U32; + sqe->off = 1; + sqe->addr = (uintptr_t) event->futex; + sqe->len = 0; + sqe->futex_flags = 0; + sqe->addr3 = FUTEX_BITSET_MATCH_ANY; + break; + case SW_IORING_OP_FUTEX_WAKE: + sqe->opcode = SW_IORING_OP_FUTEX_WAKE; + sqe->fd = FUTEX2_SIZE_U32; + sqe->off = 1; + sqe->addr = (uintptr_t) event->futex; + sqe->len = 0; + sqe->futex_flags = 0; + sqe->addr3 = FUTEX_BITSET_MATCH_ANY; + break; +#endif + default: + abort(); + return false; + } + + return submit(event); +} + +#define INIT_EVENT(op) \ + IouringEvent event{}; \ + event.coroutine = Coroutine::get_current_safe(); \ + event.opcode = op; + +int Iouring::open(const char *pathname, int flags, int mode) { + INIT_EVENT(SW_IORING_OP_OPENAT); + event.mode = mode; + event.flags = flags; + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::close(int fd) { + INIT_EVENT(SW_IORING_OP_CLOSE); + event.fd = fd; + + return execute(&event); +} + +ssize_t Iouring::read(int fd, void *buf, size_t size) { + INIT_EVENT(SW_IORING_OP_READ); + event.fd = fd; + event.rbuf = buf; + event.size = size; + + return execute(&event); +} + +ssize_t Iouring::write(int fd, const void *buf, size_t size) { + INIT_EVENT(SW_IORING_OP_WRITE); + event.fd = fd; + event.wbuf = buf; + event.size = size; + + return execute(&event); +} + +ssize_t Iouring::rename(const char *oldpath, const char *newpath) { + INIT_EVENT(SW_IORING_OP_RENAMEAT); + event.pathname = oldpath; + event.pathname2 = newpath; + + return execute(&event); +} + +int Iouring::mkdir(const char *pathname, mode_t mode) { + INIT_EVENT(SW_IORING_OP_MKDIRAT); + event.pathname = pathname; + event.mode = mode; + + return execute(&event); +} + +int Iouring::unlink(const char *pathname) { + INIT_EVENT(SW_IORING_OP_UNLINK_FILE); + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::rmdir(const char *pathname) { + INIT_EVENT(SW_IORING_OP_UNLINK_DIR); + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::fsync(int fd) { + INIT_EVENT(SW_IORING_OP_FSYNC); + event.fd = fd; + + return execute(&event); +} + +int Iouring::fdatasync(int fd) { + INIT_EVENT(SW_IORING_OP_FDATASYNC); + event.fd = fd; + + return execute(&event); +} + +#ifdef HAVE_IOURING_STATX +static void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { + statbuf->st_dev = (((unsigned int) statxbuf->stx_dev_major) << 8) | (unsigned int) statxbuf->stx_dev_minor; + statbuf->st_mode = statxbuf->stx_mode; + statbuf->st_nlink = statxbuf->stx_nlink; + statbuf->st_uid = statxbuf->stx_uid; + statbuf->st_gid = statxbuf->stx_gid; + statbuf->st_rdev = (((unsigned int) statxbuf->stx_rdev_major) << 8) | (unsigned int) statxbuf->stx_rdev_minor; + statbuf->st_ino = statxbuf->stx_ino; + statbuf->st_size = statxbuf->stx_size; + statbuf->st_blksize = statxbuf->stx_blksize; + statbuf->st_blocks = statxbuf->stx_blocks; + statbuf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec; + statbuf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec; + statbuf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec; + statbuf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec; + statbuf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec; + statbuf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec; +} + +int Iouring::fstat(int fd, struct stat *statbuf) { + struct statx _statxbuf; + INIT_EVENT(SW_IORING_OP_FSTAT); + event.fd = fd; + event.statxbuf = &_statxbuf; + + auto retval = execute(&event); + if (retval == 0) { + swoole_statx_to_stat(&_statxbuf, statbuf); + } + return retval; +} + +int Iouring::stat(const char *path, struct stat *statbuf) { + struct statx _statxbuf; + INIT_EVENT(SW_IORING_OP_LSTAT); + event.pathname = path; + event.statxbuf = &_statxbuf; + + auto retval = execute(&event); + if (retval == 0) { + swoole_statx_to_stat(&_statxbuf, statbuf); + } + return retval; +} +#endif + +#ifdef HAVE_IOURING_FUTEX +int Iouring::futex_wait(uint32_t *futex) { + INIT_EVENT(SW_IORING_OP_FUTEX_WAIT); + event.futex = futex; + + return execute(&event); +} + +int Iouring::futex_wakeup(uint32_t *futex) { + INIT_EVENT(SW_IORING_OP_FUTEX_WAKE); + event.futex = futex; + + return execute(&event); +} +#endif + +int Iouring::callback(Reactor *reactor, Event *event) { + Iouring *iouring = static_cast(event->socket->object); + return iouring->wakeup() ? SW_OK : SW_ERR; +} +} // namespace swoole +#endif diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index f26810be35b..464780b3b21 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -112,6 +112,31 @@ bool Socket::add_event(const EventType event) { return ret; } +#ifdef SW_LOG_TRACE_OPEN +static const char *get_trigger_event_name(Socket *socket, EventType added_event) { + if (socket->is_closed()) { + return "CLOSE"; + } + if (socket->errCode) { + return socket->errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR"; + } + return added_event == SW_EVENT_READ ? "READ" : "WRITE"; +} + +static const char *get_wait_event_name(Socket *socket, EventType event) { +#ifdef SW_USE_OPENSSL + if (socket->get_socket()->ssl_want_read) { + return "SSL READ"; + } else if (socket->get_socket()->ssl_want_write) { + return "SSL WRITE"; + } else +#endif + { + return event == SW_EVENT_READ ? "READ" : "WRITE"; + } +} +#endif + /** * If an exception occurs while waiting for an event, false is returned. * For example, when waiting for a read event, timeout, connection closed, are exceptions to the interrupt event. @@ -125,6 +150,10 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { if (!co) { return false; } + if (sw_unlikely(socket->close_wait)) { + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); + return false; + } // clear the last errCode set_err(0); @@ -145,18 +174,12 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { return false; } swoole_trace_log(SW_TRACE_SOCKET, - "socket#%d blongs to cid#%ld is waiting for %s event", - sock_fd, - co->get_cid(), -#ifdef SW_USE_OPENSSL - socket->ssl_want_read ? "SSL READ" - : socket->ssl_want_write ? "SSL WRITE" : -#endif - event == SW_EVENT_READ ? "READ" : "WRITE"); + "socket#%d blongs to cid#%ld is waiting for %s event", + sock_fd, + co->get_cid(), + get_wait_event_name(this, event)); - Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co){ - return cancel(event); - }; + Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co) { return cancel(event); }; if (sw_likely(event == SW_EVENT_READ)) { read_co = co; @@ -195,13 +218,11 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { want_event = SW_EVENT_NULL; #endif swoole_trace_log(SW_TRACE_SOCKET, - "socket#%d blongs to cid#%ld trigger %s event", - sock_fd, - co->get_cid(), - closed ? "CLOSE" - : errCode ? errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR" - : added_event == SW_EVENT_READ ? "READ" : "WRITE"); - return !closed && !errCode; + "socket#%d blongs to cid#%ld trigger %s event", + sock_fd, + co->get_cid(), + get_trigger_event_name(this, added_event)); + return !is_closed() && !errCode; } bool Socket::socks5_handshake() { @@ -330,20 +351,10 @@ bool Socket::socks5_handshake() { } bool Socket::http_proxy_handshake() { -#define HTTP_PROXY_FMT \ - "CONNECT %.*s:%d HTTP/1.1\r\n" \ - "Host: %.*s:%d\r\n" \ - "User-Agent: Swoole/" SWOOLE_VERSION "\r\n" \ - "Proxy-Connection: Keep-Alive\r\n" - - // CONNECT - int n; - const char *host = http_proxy->target_host.c_str(); - int host_len = http_proxy->target_host.length(); + const std::string *real_host = &http_proxy->target_host; #ifdef SW_USE_OPENSSL if (ssl_context && !ssl_context->tls_host_name.empty()) { - host = ssl_context->tls_host_name.c_str(); - host_len = ssl_context->tls_host_name.length(); + real_host = &ssl_context->tls_host_name; } #endif @@ -352,34 +363,11 @@ bool Socket::http_proxy_handshake() { send_buffer->clear(); }; - if (!http_proxy->password.empty()) { - auto auth_str = http_proxy->get_auth_str(); - n = sw_snprintf(send_buffer->str, - send_buffer->size, - HTTP_PROXY_FMT "Proxy-Authorization: Basic %s\r\n\r\n", - (int) http_proxy->target_host.length(), - http_proxy->target_host.c_str(), - http_proxy->target_port, - host_len, - host, - http_proxy->target_port, - auth_str.c_str()); - } else { - n = sw_snprintf(send_buffer->str, - send_buffer->size, - HTTP_PROXY_FMT "\r\n", - (int) http_proxy->target_host.length(), - http_proxy->target_host.c_str(), - http_proxy->target_port, - host_len, - host, - http_proxy->target_port); - } - - swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); - + size_t n = http_proxy->pack(send_buffer, real_host); send_buffer->length = n; - if (send(send_buffer->str, n) != n) { + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + + if (send(send_buffer->str, n) != (ssize_t) n) { return false; } @@ -394,70 +382,30 @@ bool Socket::http_proxy_handshake() { protocol.package_eof_len = sizeof("\r\n\r\n") - 1; memcpy(protocol.package_eof, SW_STRS("\r\n\r\n")); - n = recv_packet(); - if (n <= 0) { + if (recv_packet() <= 0) { return false; } swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy response: <str); - bool ret = false; - char *buf = recv_buffer->str; - int len = n; - int state = 0; - char *p = buf; - char *pe = buf + len; - for (; p < buf + len; p++) { - if (state == 0) { - if (SW_STRCASECT(p, pe - p, "HTTP/1.1") || SW_STRCASECT(p, pe - p, "HTTP/1.0")) { - state = 1; - p += sizeof("HTTP/1.x") - 1; - } else { - break; - } - } else if (state == 1) { - if (isspace(*p)) { - continue; - } else { - if (SW_STRCASECT(p, pe - p, "200")) { - state = 2; - p += sizeof("200") - 1; - } else { - break; - } - } - } else if (state == 2) { - ret = true; - break; -#if 0 - if (isspace(*p)) { - continue; - } else { - if (SW_STRCASECT(p, pe - p, "Connection established")) { - ret = true; - } - break; - } -#endif - } - } - - if (!ret) { + if (!http_proxy->handshake(recv_buffer)) { set_err(SW_ERROR_HTTP_PROXY_BAD_RESPONSE, std::string("wrong http_proxy response received, \n[Request]: ") + send_buffer->to_std_string() + - "\n[Response]: " + std::string(buf, len)); + "\n[Response]: " + send_buffer->to_std_string()); + return false; } - return ret; + return true; } -void Socket::init_sock_type(SocketType _sw_type) { - type = _sw_type; - network::Socket::get_domain_and_type(_sw_type, &sock_domain, &sock_type); +void Socket::init_sock_type(SocketType _type) { + type = _type; + network::Socket::get_domain_and_type(_type, &sock_domain, &sock_type); } bool Socket::init_sock() { - socket = make_socket(type, SW_FD_CO_SOCKET, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + socket = + make_socket(type, SW_FD_CO_SOCKET, sock_domain, sock_type, sock_protocol, SW_SOCK_NONBLOCK | SW_SOCK_CLOEXEC); if (socket == nullptr) { return false; } @@ -480,7 +428,7 @@ bool Socket::init_reactor_socket(int _fd) { Socket::Socket(int _domain, int _type, int _protocol) : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) { - type = network::Socket::convert_to_type(_domain, _type, _protocol); + type = network::Socket::convert_to_type(_domain, _type); if (sw_unlikely(!init_sock())) { return; } @@ -496,17 +444,20 @@ Socket::Socket(SocketType _type) { } Socket::Socket(int _fd, SocketType _type) { - init_sock_type(_type); if (sw_unlikely(!init_reactor_socket(_fd))) { return; } + if (_type == SW_SOCK_RAW) { + return; + } + init_sock_type(_type); socket->set_nonblock(); init_options(); } Socket::Socket(int _fd, int _domain, int _type, int _protocol) : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) { - type = network::Socket::convert_to_type(_domain, _type, _protocol); + type = network::Socket::convert_to_type(_domain, _type); if (sw_unlikely(!init_reactor_socket(_fd))) { return; } @@ -582,7 +533,7 @@ bool Socket::connect(const struct sockaddr *addr, socklen_t addrlen) { } else { TimerController timer(&write_timer, connect_timeout, this, timer_callback); if (!timer.start() || !wait_event(SW_EVENT_WRITE)) { - if (closed) { + if (is_closed()) { set_err(ECONNABORTED); } return false; @@ -634,7 +585,7 @@ bool Socket::connect(std::string _host, int _port, int flags) { _port = http_proxy->proxy_port; } - if (sock_domain == AF_INET6 || sock_domain == AF_INET) { + if (is_port_required()) { if (_port == -1) { set_err(EINVAL, "Socket of type AF_INET/AF_INET6 requires port argument"); return false; @@ -648,23 +599,54 @@ bool Socket::connect(std::string _host, int _port, int flags) { connect_port = _port; struct sockaddr *_target_addr = nullptr; + NameResolver::Context *ctx = resolve_context_; + + NameResolver::Context _ctx{}; + if (ctx == nullptr) { + ctx = &_ctx; + } + ctx->timeout = dns_timeout; + + std::once_flag oc; + auto name_resolve_fn = [ctx, &oc, this](int type) -> bool { + ctx->type = type; +#ifdef SW_USE_OPENSSL + std::call_once(oc, [this]() { + if (ssl_context && !(socks5_proxy || http_proxy)) { + ssl_host_name = connect_host; + } + }); +#endif + /* locked like wait_event */ + read_co = write_co = Coroutine::get_current_safe(); + ON_SCOPE_EXIT { + read_co = write_co = nullptr; + }; + std::string addr = swoole_name_resolver_lookup(connect_host, ctx); + if (addr.empty()) { + set_err(swoole_get_last_error()); + return false; + } + if (ctx->with_port) { + char delimiter = type == AF_INET6 ? '@' : ':'; + auto port_pos = addr.find_first_of(delimiter); + if (port_pos != addr.npos) { + connect_port = std::stoi(addr.substr(port_pos + 1)); + connect_host = addr.substr(0, port_pos); + return true; + } + } + connect_host = addr; + return true; + }; for (int i = 0; i < 2; i++) { if (sock_domain == AF_INET) { socket->info.addr.inet_v4.sin_family = AF_INET; - socket->info.addr.inet_v4.sin_port = htons(_port); + socket->info.addr.inet_v4.sin_port = htons(connect_port); if (!inet_pton(AF_INET, connect_host.c_str(), &socket->info.addr.inet_v4.sin_addr)) { -#ifdef SW_USE_OPENSSL - if (ssl_context && !(socks5_proxy || http_proxy)) { - ssl_host_name = connect_host; - } -#endif - /* locked like wait_event */ - read_co = write_co = Coroutine::get_current_safe(); - connect_host = System::gethostbyname(connect_host, AF_INET, dns_timeout); - read_co = write_co = nullptr; - if (connect_host.empty()) { + if (!name_resolve_fn(AF_INET)) { set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error())); return false; } @@ -676,16 +658,10 @@ bool Socket::connect(std::string _host, int _port, int flags) { } } else if (sock_domain == AF_INET6) { socket->info.addr.inet_v6.sin6_family = AF_INET6; - socket->info.addr.inet_v6.sin6_port = htons(_port); + socket->info.addr.inet_v6.sin6_port = htons(connect_port); if (!inet_pton(AF_INET6, connect_host.c_str(), &socket->info.addr.inet_v6.sin6_addr)) { -#ifdef SW_USE_OPENSSL - if (ssl_context && !(socks5_proxy || http_proxy)) { - ssl_host_name = connect_host; - } -#endif - connect_host = System::gethostbyname(connect_host, AF_INET6, dns_timeout); - if (connect_host.empty()) { + if (!name_resolve_fn(AF_INET6)) { set_err(swoole_get_last_error()); return false; } @@ -702,7 +678,7 @@ bool Socket::connect(std::string _host, int _port, int flags) { } socket->info.addr.un.sun_family = AF_UNIX; memcpy(&socket->info.addr.un.sun_path, connect_host.c_str(), connect_host.size()); - socket->info.len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + connect_host.size()); + socket->info.len = (socklen_t) (offsetof(struct sockaddr_un, sun_path) + connect_host.size()); _target_addr = (struct sockaddr *) &socket->info.addr.un; break; } else { @@ -747,8 +723,8 @@ bool Socket::connect(std::string _host, int _port, int flags) { } bool Socket::check_liveness() { - if (closed) { - set_err(ECONNRESET); + if (is_closed()) { + set_err(EBADF); return false; } if (!socket->check_liveness()) { @@ -765,11 +741,15 @@ ssize_t Socket::peek(void *__buf, size_t __n) { return retval; } -bool Socket::poll(EventType type) { +bool Socket::poll(EventType type, double timeout) { if (sw_unlikely(!is_available(type))) { return -1; } - TimerController timer(&read_timer, read_timeout, this, timer_callback); + TimerNode **timer_pp = type == SW_EVENT_READ ? &read_timer : &write_timer; + if (timeout == 0) { + timeout = type == SW_EVENT_READ ? read_timeout : write_timeout; + } + TimerController timer(timer_pp, timeout, this, timer_callback); if (timer.start() && wait_event(type)) { return true; } else { @@ -785,7 +765,7 @@ ssize_t Socket::recv(void *__buf, size_t __n) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->recv(__buf, __n, 0); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -798,7 +778,7 @@ ssize_t Socket::send(const void *__buf, size_t __n) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->send(__buf, __n, 0); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); check_return_value(retval); return retval; @@ -812,7 +792,7 @@ ssize_t Socket::read(void *__buf, size_t __n) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->read(__buf, __n); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -887,7 +867,7 @@ ssize_t Socket::write(const void *__buf, size_t __n) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->write((void *) __buf, __n); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); check_return_value(retval); return retval; @@ -901,7 +881,7 @@ ssize_t Socket::readv(network::IOVector *io_vector) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->readv(io_vector); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; @@ -917,7 +897,7 @@ ssize_t Socket::readv_all(network::IOVector *io_vector) { retval = socket->readv(io_vector); swoole_trace_log(SW_TRACE_SOCKET, "readv %ld bytes, errno=%d", retval, errno); - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -943,7 +923,7 @@ ssize_t Socket::readv_all(network::IOVector *io_vector) { total_bytes += retval; } while (retval > 0 && io_vector->get_remain_count() > 0); - return retval < 0 && socket->catch_error(errno) == SW_WAIT; + return retval < 0 && socket->catch_read_error(errno) == SW_WAIT; }; recv_barrier = &barrier; @@ -963,7 +943,7 @@ ssize_t Socket::writev(network::IOVector *io_vector) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->writev(io_vector); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); check_return_value(retval); return retval; @@ -979,7 +959,7 @@ ssize_t Socket::writev_all(network::IOVector *io_vector) { retval = socket->writev(io_vector); swoole_trace_log(SW_TRACE_SOCKET, "writev %ld bytes, errno=%d", retval, errno); - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1005,7 +985,7 @@ ssize_t Socket::writev_all(network::IOVector *io_vector) { total_bytes += retval; } while (retval > 0 && io_vector->get_remain_count() > 0); - return retval < 0 && socket->catch_error(errno) == SW_WAIT; + return retval < 0 && socket->catch_write_error(errno) == SW_WAIT; }; send_barrier = &barrier; @@ -1030,7 +1010,7 @@ ssize_t Socket::recv_all(void *__buf, size_t __n) { if (retval == 0 || retval == (ssize_t) __n) { return retval; } - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1040,7 +1020,8 @@ ssize_t Socket::recv_all(void *__buf, size_t __n) { EventBarrier barrier = [&__n, &total_bytes, &retval, &__buf, this]() -> bool { retval = socket->recv((char *) __buf + total_bytes, __n - total_bytes, 0); - return (retval < 0 && socket->catch_error(errno) == SW_WAIT) || (retval > 0 && (total_bytes += retval) < __n); + return (retval < 0 && socket->catch_read_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); }; recv_barrier = &barrier; @@ -1065,7 +1046,7 @@ ssize_t Socket::send_all(const void *__buf, size_t __n) { if (retval == 0 || retval == (ssize_t) __n) { return retval; } - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1075,7 +1056,8 @@ ssize_t Socket::send_all(const void *__buf, size_t __n) { EventBarrier barrier = [&__n, &total_bytes, &retval, &__buf, this]() -> bool { retval = socket->send((char *) __buf + total_bytes, __n - total_bytes, 0); - return (retval < 0 && socket->catch_error(errno) == SW_WAIT) || (retval > 0 && (total_bytes += retval) < __n); + return (retval < 0 && socket->catch_write_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); }; send_barrier = &barrier; @@ -1095,7 +1077,7 @@ ssize_t Socket::recvmsg(struct msghdr *msg, int flags) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = ::recvmsg(sock_fd, msg, flags); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -1111,7 +1093,7 @@ ssize_t Socket::sendmsg(const struct msghdr *msg, int flags) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = ::sendmsg(sock_fd, msg, flags); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); check_return_value(retval); return retval; } @@ -1164,6 +1146,11 @@ Socket *Socket::accept(double timeout) { if (sw_unlikely(!is_available(SW_EVENT_READ))) { return nullptr; } +#ifdef SW_USE_OPENSSL + if (ssl_is_enable() && sw_unlikely(ssl_context->context == nullptr) && !ssl_context_create()) { + return nullptr; + } +#endif network::Socket *conn = socket->accept(); if (conn == nullptr && errno == EAGAIN) { TimerController timer(&read_timer, timeout == 0 ? read_timeout : timeout, this, timer_callback); @@ -1189,10 +1176,7 @@ Socket *Socket::accept(double timeout) { } #ifdef SW_USE_OPENSSL -bool Socket::ssl_check_context() { - if (socket->ssl || (get_ssl_context() && get_ssl_context()->get_context())) { - return true; - } +bool Socket::ssl_context_create() { if (socket->is_dgram()) { #ifdef SW_SUPPORT_DTLS socket->dtls = 1; @@ -1205,7 +1189,6 @@ bool Socket::ssl_check_context() { } ssl_context->http_v2 = http2; if (!ssl_context->create()) { - swoole_warning("swSSL_get_context() error"); return false; } socket->ssl_send_ = 1; @@ -1239,12 +1222,20 @@ bool Socket::ssl_handshake() { if (sw_unlikely(!is_available(SW_EVENT_RDWR))) { return false; } - if (!ssl_check_context()) { + /** + * If the ssl_context is empty, it indicates that this socket was not a connection + * returned by a server socket accept, and a new ssl_context needs to be created. + */ + if (ssl_context->context == nullptr && !ssl_context_create()) { return false; } if (!ssl_create(get_ssl_context())) { return false; } + /** + * The server will use ssl_accept to complete the SSL handshake, + * while the client will use ssl_connect. + */ if (!ssl_is_server) { while (true) { if (socket->ssl_connect() < 0) { @@ -1299,6 +1290,7 @@ bool Socket::ssl_verify(bool allow_self_signed) { std::string Socket::ssl_get_peer_cert() { if (!socket->ssl_get_peer_certificate(sw_tg_buffer())) { + set_err(SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE); return ""; } else { return sw_tg_buffer()->to_std_string(); @@ -1330,16 +1322,16 @@ bool Socket::sendfile(const char *filename, off_t offset, size_t length) { } TimerController timer(&write_timer, write_timeout, this, timer_callback); - int n, sendn; + ssize_t n, sent_bytes; while ((size_t) offset < length) { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; + sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; #ifdef SW_USE_OPENSSL if (socket->ssl) { - n = socket->ssl_sendfile(file, &offset, sendn); + n = socket->ssl_sendfile(file, &offset, sent_bytes); } else #endif { - n = ::swoole_sendfile(sock_fd, file.get_fd(), &offset, sendn); + n = ::swoole_sendfile(sock_fd, file.get_fd(), &offset, sent_bytes); } if (n > 0) { continue; @@ -1422,7 +1414,7 @@ ssize_t Socket::sendto(const std::string &host, int port, const void *__buf, siz do { retval = ::sendto(sock_fd, __buf, __n, 0, (struct sockaddr *) &addr, addr_size); swoole_trace_log(SW_TRACE_SOCKET, "sendto %ld/%ld bytes, errno=%d", retval, __n, errno); - } while (retval < 0 && (errno == EINTR || (socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && (errno == EINTR || (socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)))); check_return_value(retval); } @@ -1447,8 +1439,8 @@ ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, sockle do { retval = ::recvfrom(sock_fd, __buf, __n, 0, _addr, _socklen); swoole_trace_log(SW_TRACE_SOCKET, "recvfrom %ld/%ld bytes, errno=%d", retval, __n, errno); - } while (retval < 0 && ((errno == EINTR) || - (socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)))); + } while (retval < 0 && ((errno == EINTR) || (socket->catch_read_error(errno) == SW_WAIT && timer.start() && + wait_event(SW_EVENT_READ)))); check_return_value(retval); return retval; } @@ -1456,6 +1448,7 @@ ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, sockle ssize_t Socket::recv_packet_with_length_protocol() { ssize_t packet_len = SW_BUFFER_SIZE_STD; ssize_t retval; + PacketLength pl; uint32_t header_len = protocol.package_length_offset + protocol.package_length_size; if (read_buffer->length > 0) { @@ -1477,25 +1470,27 @@ ssize_t Socket::recv_packet_with_length_protocol() { } _get_length: - protocol.real_header_length = 0; - packet_len = protocol.get_package_length(&protocol, socket, read_buffer->str, (uint32_t) read_buffer->length); + pl.header_len = 0; + pl.buf = read_buffer->str; + pl.buf_size = (uint32_t) read_buffer->length; + packet_len = protocol.get_package_length(&protocol, socket, &pl); swoole_trace_log(SW_TRACE_SOCKET, "packet_len=%ld, length=%ld", packet_len, read_buffer->length); if (packet_len < 0) { set_err(SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, "get package length failed"); return 0; } else if (packet_len == 0) { - if (protocol.real_header_length != 0) { - header_len = protocol.real_header_length; + if (pl.header_len != 0) { + header_len = pl.header_len; } goto _recv_header; } else if (packet_len > protocol.package_max_length) { read_buffer->clear(); swoole_error_log(SW_LOG_WARNING, - SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, - "packet length is too big, remote_addr=%s:%d, length=%zu", - socket->info.get_ip(), - socket->info.get_port(), - packet_len); + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "packet length is too big, remote_addr=%s:%d, length=%zu", + socket->info.get_ip(), + socket->info.get_port(), + packet_len); set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, sw_error); return -1; } @@ -1687,41 +1682,39 @@ bool Socket::cancel(const EventType event) { write_co->resume(); return true; } else { + set_err(EINVAL); return false; } } /** - * @return bool (whether it can be freed) - * you can access errCode member to get error information + * @return bool + * If true is returned, the related resources of this socket can be released + * If false is returned, it means that other coroutines are still referencing this socket, + * and need to wait for the coroutine bound to readable or writable event to execute close, + * and release when all references are 0 */ bool Socket::close() { - if (sock_fd < 0) { + if (is_closed()) { set_err(EBADF); - return true; + return false; } if (connected) { shutdown(); } if (sw_unlikely(has_bound())) { - if (closed) { - // close operation is in processing - set_err(EINPROGRESS); - return false; - } - closed = true; - if (write_co) { - set_err(ECONNRESET); - write_co->resume(); - } - if (read_co) { - set_err(ECONNRESET); - read_co->resume(); - } + socket->close_wait = 1; + cancel(SW_EVENT_WRITE); + cancel(SW_EVENT_READ); + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); return false; } else { - sock_fd = -1; - closed = true; + sock_fd = SW_BAD_SOCKET; + if (dtor_ != nullptr) { + auto dtor = dtor_; + dtor_ = nullptr; + dtor(this); + } return true; } } @@ -1765,23 +1758,11 @@ Socket::~Socket() { if (socket->socket_type == SW_SOCK_UNIX_DGRAM) { ::unlink(socket->info.addr.un.sun_path); } + if (dtor_ != nullptr) { + dtor_(this); + } socket->free(); } } // namespace coroutine - -std::string HttpProxy::get_auth_str() { - char auth_buf[256]; - char encode_buf[512]; - size_t n = sw_snprintf(auth_buf, - sizeof(auth_buf), - "%.*s:%.*s", - (int) username.length(), - username.c_str(), - (int) password.length(), - password.c_str()); - base64_encode((unsigned char *) auth_buf, n, encode_buf); - return std::string(encode_buf); -} - } // namespace swoole diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index f7a042873ac..cc6d5856eb8 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,12 +19,18 @@ #include "swoole_lru_cache.h" #include "swoole_signal.h" +#ifdef SW_USE_IOURING +#include "swoole_iouring.h" +using swoole::Iouring; +#endif + namespace swoole { namespace coroutine { static size_t dns_cache_capacity = 1000; static time_t dns_cache_expire = 60; static LRUCache *dns_cache = nullptr; +static std::unordered_map async_resource_map; void System::set_dns_cache_expire(time_t expire) { dns_cache_expire = expire; @@ -136,21 +142,8 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { AsyncEvent ev{}; - - if (hostname.size() < SW_IP_MAX_LENGTH) { - ev.nbytes = SW_IP_MAX_LENGTH + 1; - } else { - ev.nbytes = hostname.size() + 1; - } - - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) { - return ""; - } - - memcpy(ev.buf, hostname.c_str(), hostname.size()); - ((char *) ev.buf)[hostname.size()] = 0; - ev.flags = domain; + auto req = new GethostbynameRequest(hostname, domain); + ev.data = std::shared_ptr(req); ev.retval = 1; coroutine::async(async::handler_gethostbyname, ev, timeout); @@ -162,8 +155,7 @@ std::string gethostbyname_impl_with_async(const std::string &hostname, int domai swoole_set_last_error(ev.error); return ""; } else { - std::string addr((char *) ev.buf); - sw_free(ev.buf); + std::string addr(req->addr); return addr; } } @@ -212,107 +204,103 @@ std::vector System::getaddrinfo( assert(family == AF_INET || family == AF_INET6); AsyncEvent ev{}; - network::GetaddrinfoRequest req{}; - - ev.req = &req; - - struct sockaddr_in6 result_buffer[SW_DNS_HOST_BUFFER_SIZE]; - - req.hostname = hostname.c_str(); - req.family = family; - req.socktype = socktype; - req.protocol = protocol; - req.service = service.empty() ? nullptr : service.c_str(); - req.result = result_buffer; + auto req = new GetaddrinfoRequest(hostname, family, socktype, protocol, service); + ev.data = std::shared_ptr(req); coroutine::async(async::handler_getaddrinfo, ev, timeout); std::vector retval; - if (ev.retval == -1 || req.error != 0) { + if (ev.retval == -1 || req->error != 0) { if (ev.error == SW_ERROR_AIO_TIMEOUT) { ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; } swoole_set_last_error(ev.error); } else { - req.parse_result(retval); + req->parse_result(retval); } return retval; } +struct SignalListener { + Coroutine *co; + int signo; +}; + +/** + * Only the main thread should listen for signals, + * without modifying it to a thread-local variable. + */ +static SignalListener *listeners[SW_SIGNO_MAX]; + +int System::wait_signal(int signal, double timeout) { + std::vector signals = {signal}; + return wait_signal(signals, timeout); +} + /** * @error: swoole_get_last_error() */ -bool System::wait_signal(int signo, double timeout) { - static Coroutine *listeners[SW_SIGNO_MAX]; - Coroutine *co = Coroutine::get_current_safe(); +int System::wait_signal(const std::vector &signals, double timeout) { + SignalListener listener = { + Coroutine::get_current_safe(), + -1, + }; if (SwooleTG.signal_listener_num > 0) { swoole_set_last_error(EBUSY); - return false; + return -1; } - if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { - swoole_set_last_error(EINVAL); - return false; + + auto callback_fn = [](int signo) { + auto listener = listeners[signo]; + if (listener) { + listeners[signo] = nullptr; + listener->signo = signo; + listener->co->resume(); + } + }; + + for (auto &signo : signals) { + if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { + swoole_set_last_error(EINVAL); + return -1; + } + + /* resgiter signal */ + listeners[signo] = &listener; + +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, &callback_fn](void *) { swoole_signal_set(signo, callback_fn); }, nullptr); +#else + swoole_signal_set(signo, callback_fn); +#endif } - /* resgiter signal */ - listeners[signo] = co; - // for swSignalfd_setup - sw_reactor()->check_signalfd = true; // exit condition if (!sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER)) { sw_reactor()->set_exit_condition( Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER, [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } - /* always enable signalfd */ - SwooleG.use_signalfd = SwooleG.enable_signalfd = 1; - swoole_signal_set(signo, [](int signo) { - Coroutine *co = listeners[signo]; - if (co) { - listeners[signo] = nullptr; - co->resume(); - } - }); - SwooleTG.co_signal_listener_num++; - TimerNode *timer = nullptr; - if (timeout > 0) { - timer = swoole_timer_add( - timeout * 1000, - 0, - [](Timer *timer, TimerNode *tnode) { - Coroutine *co = (Coroutine *) tnode->data; - co->resume(); - }, - co); - } - - Coroutine::CancelFunc cancel_fn = [timer](Coroutine *co) { - if (timer) { - swoole_timer_del(timer); - } - co->resume(); - return true; - }; - co->yield(&cancel_fn); + SwooleTG.co_signal_listener_num++; - swoole_signal_set(signo, nullptr); - SwooleTG.co_signal_listener_num--; + bool retval = listener.co->yield_ex(timeout); - if (listeners[signo] != nullptr) { + for (auto &signo : signals) { +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); +#else + swoole_signal_set(signo, nullptr); +#endif listeners[signo] = nullptr; - swoole_set_last_error(co->is_canceled() ? SW_ERROR_CO_CANCELED : ETIMEDOUT); - return false; } - if (timer) { - swoole_timer_del(timer); - } + SwooleTG.co_signal_listener_num--; - return !co->is_canceled(); + return retval ? listener.signo : -1; } struct CoroPollTask { @@ -395,7 +383,7 @@ static int socket_poll_error_callback(Reactor *reactor, Event *event) { return SW_OK; } -static int translate_events_to_poll(int events) { +int translate_events_to_poll(int events) { int poll_events = 0; if (events & SW_EVENT_READ) { @@ -408,7 +396,7 @@ static int translate_events_to_poll(int events) { return poll_events; } -static int translate_events_from_poll(int events) { +int translate_events_from_poll(int events) { int sw_events = 0; if (events & POLLIN) { @@ -469,7 +457,7 @@ bool System::socket_poll(std::unordered_map &fds, double timeou } if (timeout > 0) { - task.timer = swoole_timer_add((long) (timeout * 1000), false, socket_poll_timeout, &task); + task.timer = swoole_timer_add(timeout, false, socket_poll_timeout, &task); } task.co->yield(); @@ -506,15 +494,16 @@ struct EventWaiter { } if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), - false, - [](Timer *timer, TimerNode *tnode) { - EventWaiter *waiter = (EventWaiter *) tnode->data; - waiter->timer = nullptr; - waiter->error_ = ETIMEDOUT; - waiter->co->resume(); - }, - this); + timer = swoole_timer_add( + timeout, + false, + [](Timer *timer, TimerNode *tnode) { + EventWaiter *waiter = (EventWaiter *) tnode->data; + waiter->timer = nullptr; + waiter->error_ = ETIMEDOUT; + waiter->co->resume(); + }, + this); } co->yield(&cancel_fn); @@ -600,6 +589,35 @@ int System::wait_event(int fd, int events, double timeout) { return revents; } +bool System::exec(const char *command, bool get_error_stream, std::shared_ptr buffer, int *status) { + Coroutine::get_current_safe(); + + pid_t pid; + int fd = swoole_shell_exec(command, &pid, get_error_stream); + if (fd < 0) { + swoole_sys_warning("Unable to execute '%s'", command); + return false; + } + + Socket socket(fd, SW_SOCK_UNIX_STREAM); + while (1) { + ssize_t retval = socket.read(buffer->str + buffer->length, buffer->size - buffer->length); + if (retval > 0) { + buffer->length += retval; + if (buffer->length == buffer->size) { + if (!buffer->extend()) { + break; + } + } + } else { + break; + } + } + socket.close(); + + return System::waitpid_safe(pid, status, 0) == pid; +} + void System::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_READ, socket_poll_read_callback); reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_WRITE, socket_poll_write_callback); @@ -610,6 +628,9 @@ void System::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_ERROR, event_waiter_error_callback); reactor->set_handler(SW_FD_AIO | SW_EVENT_READ, AsyncThreads::callback); +#ifdef SW_USE_IOURING + reactor->set_handler(SW_FD_IOURING | SW_EVENT_READ, Iouring::callback); +#endif } static void async_task_completed(AsyncEvent *event) { @@ -642,7 +663,7 @@ bool async(async::Handler handler, AsyncEvent &event, double timeout) { return false; } else { event.canceled = _ev->canceled; - event.error = errno = _ev->error; + event.error = errno = _ev->error; event.retval = _ev->retval; return true; } @@ -661,14 +682,11 @@ static void async_lambda_handler(AsyncEvent *event) { } static void async_lambda_callback(AsyncEvent *event) { - if (event->canceled) { - return; - } AsyncLambdaTask *task = reinterpret_cast(event->object); task->co->resume(); } -bool async(const std::function &fn, double timeout) { +bool async(const std::function &fn) { AsyncEvent event{}; AsyncLambdaTask task{Coroutine::get_current_safe(), fn}; @@ -681,14 +699,46 @@ bool async(const std::function &fn, double timeout) { return false; } - if (!task.co->yield_ex(timeout)) { - _ev->canceled = true; - errno = swoole_get_last_error(); - return false; - } else { - errno = _ev->error; - return true; + task.co->yield(); + errno = _ev->error; + return true; +} + +AsyncLock::AsyncLock(void *resource) { + resource_ = resource; + async_resource_map.emplace(resource, Coroutine::get_current_cid()); +} + +AsyncLock::~AsyncLock() { + async_resource_map.erase(resource_); +} + +std::shared_ptr async_lock(void *resource) { + auto iter = async_resource_map.find(resource); + if (iter != async_resource_map.end()) { + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, + "resource(%p) has already been bound to another coroutine#%ld, " + "%s of the same resource in coroutine#%ld at the same time is not allowed", + resource, + *iter, + Coroutine::get_current_cid()); + return nullptr; + } + return std::make_shared(resource); +} + +bool wait_for(const std::function &fn) { + double second = 0.001; + while (true) { + if (fn()) { + break; + } + if (System::sleep(second) != SW_OK) { + return false; + } + second *= 2; } + return true; } } // namespace coroutine diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index ec5e0ed7382..b670d495719 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -10,12 +10,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_api.h" #include "swoole_async.h" +#include "swoole_signal.h" #include "swoole_coroutine_context.h" #ifdef SW_USE_THREAD_CONTEXT @@ -32,9 +33,13 @@ static std::mutex *current_lock = nullptr; void thread_context_init() { if (!swoole_timer_is_available()) { - swoole_timer_add(1, false, [](Timer *timer, TimerNode *tnode) { - // do nothing - }, nullptr); + swoole_timer_add( + 1L, + false, + [](Timer *timer, TimerNode *tnode) { + // do nothing + }, + nullptr); } if (SwooleTG.async_threads == nullptr) { SwooleTG.async_threads = new AsyncThreads(); @@ -56,8 +61,8 @@ void thread_context_clean() { g_lock.unlock(); } -Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) - : fn_(fn), private_data_(private_data) { +Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) + : fn_(std::move(fn)), private_data_(private_data) { end_ = false; lock_.lock(); swap_lock_ = nullptr; @@ -83,7 +88,8 @@ bool Context::swap_out() { return true; } -void Context::context_func(void *arg) { +void Context::context_func(coroutine_transfer_t arg) { + swoole_signal_block_all(); Context *_this = (Context *) arg; SwooleTG.reactor = g_reactor; SwooleTG.timer = g_timer; diff --git a/src/lock/barrier.cc b/src/lock/barrier.cc new file mode 100644 index 00000000000..042d1cd66fb --- /dev/null +++ b/src/lock/barrier.cc @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "swoole.h" +#include "swoole_lock.h" + +namespace swoole { + +#define BARRIER_USEC 10000 + +void Barrier::init(bool shared, int count) { +#ifdef SW_USE_PTHREAD_BARRIER + if (shared) { + pthread_barrierattr_setpshared(&barrier_attr_, PTHREAD_PROCESS_SHARED); + pthread_barrier_init(&barrier_, &barrier_attr_, count); + } else { + pthread_barrier_init(&barrier_, nullptr, count); + } + shared_ = shared; +#else + barrier_ = 0; + count_ = count; +#endif +} + +void Barrier::wait() { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_wait(&barrier_); +#else + sw_atomic_add_fetch(&barrier_, 1); + SW_LOOP { + if (barrier_ == count_) { + break; + } + usleep(BARRIER_USEC); + sw_atomic_memory_barrier(); + } +#endif +} + +void Barrier::destroy() { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_destroy(&barrier_); + if (shared_) { + pthread_barrierattr_destroy(&barrier_attr_); + } +#endif +} + +}; // namespace swoole diff --git a/src/lock/coroutine_lock.cc b/src/lock/coroutine_lock.cc new file mode 100644 index 00000000000..cb70842bfa6 --- /dev/null +++ b/src/lock/coroutine_lock.cc @@ -0,0 +1,129 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ + +#include "swoole.h" + +#ifdef HAVE_IOURING_FUTEX +#include "swoole_iouring.h" +#else +#include "swoole_coroutine_system.h" +using swoole::coroutine::System; +#endif + +#include "swoole_lock.h" + +namespace swoole { +CoroutineLock::CoroutineLock(bool shared) : Lock() { + type_ = COROUTINE_LOCK; + shared_ = shared; + if (shared) { + value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); + } else { + value = new sw_atomic_t; + } + *value = 0; +} + +CoroutineLock::~CoroutineLock() { + if (shared_) { + sw_mem_pool()->free((void *) value); + } else { + delete value; + } + value = nullptr; +} + +int CoroutineLock::lock() { + return lock_impl(true); +} + +int CoroutineLock::trylock() { + return lock_impl(false); +} + +int CoroutineLock::lock_rd() { + return lock_impl(true); +} + +int CoroutineLock::trylock_rd() { + return lock_impl(false); +} + +int CoroutineLock::unlock() { + Coroutine *current_coroutine = Coroutine::get_current(); + if (current_coroutine == nullptr) { + swoole_warning("The coroutine lock can only be used in a coroutine environment"); + return SW_ERROR_CO_OUT_OF_COROUTINE; + } + + if (*value == 0) { + return 0; + } + + *value = 0; + cid = 0; + coroutine = nullptr; + +#ifdef HAVE_IOURING_FUTEX + return Iouring::futex_wakeup((uint32_t *) value) >= 0 ? 0 : errno; +#else + return 0; +#endif +} + +int CoroutineLock::lock_impl(bool blocking) { + Coroutine *current_coroutine = Coroutine::get_current(); + if (current_coroutine == nullptr) { + swoole_warning("The coroutine lock can only be used in a coroutine environment"); + return SW_ERROR_CO_OUT_OF_COROUTINE; + } + + if (current_coroutine == static_cast(coroutine) && current_coroutine->get_cid() == cid) { + return 0; + } + + int result = 0; +#ifndef HAVE_IOURING_FUTEX + double second = 0.001; +#endif + + while (true) { + if (sw_atomic_cmp_set(value, 0, 1)) { + break; + } + + if (!blocking) { + return EBUSY; + } + +#ifdef HAVE_IOURING_FUTEX + result = Iouring::futex_wait((uint32_t *) value); + if (result != 0) { + return errno; + } +#else + if (System::sleep(second) != SW_OK) { + return SW_ERROR_CO_CANCELED; + } + second *= 2; +#endif + } + + cid = current_coroutine->get_cid(); + coroutine = (void *) current_coroutine; + return result; +} +} // namespace swoole diff --git a/src/lock/mutex.cc b/src/lock/mutex.cc index 9ef10cc2089..afaecb3d53f 100644 --- a/src/lock/mutex.cc +++ b/src/lock/mutex.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -25,6 +25,8 @@ struct MutexImpl { }; Mutex::Mutex(int flags) : Lock() { + flags_ = flags; + if (flags & PROCESS_SHARED) { impl = (MutexImpl *) sw_mem_pool()->alloc(sizeof(*impl)); if (impl == nullptr) { @@ -55,18 +57,20 @@ Mutex::Mutex(int flags) : Lock() { #endif } - if (pthread_mutex_init(&impl->lock_, &impl->attr_) < 0) { + if (pthread_mutex_init(&impl->lock_, &impl->attr_) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_mutex_init() failed"); } } int Mutex::lock() { int retval = pthread_mutex_lock(&impl->lock_); + #ifdef HAVE_PTHREAD_MUTEX_CONSISTENT - if (retval == EOWNERDEAD) { + if (retval == EOWNERDEAD && (flags_ & ROBUST)) { retval = pthread_mutex_consistent(&impl->lock_); } #endif + return retval; } diff --git a/src/lock/rw_lock.cc b/src/lock/rw_lock.cc index 343f5e58d34..5689f45145c 100644 --- a/src/lock/rw_lock.cc +++ b/src/lock/rw_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -43,7 +43,7 @@ RWLock::RWLock(int use_in_process) : Lock() { if (use_in_process == 1) { pthread_rwlockattr_setpshared(&impl->attr, PTHREAD_PROCESS_SHARED); } - if (pthread_rwlock_init(&impl->_lock, &impl->attr) < 0) { + if (pthread_rwlock_init(&impl->_lock, &impl->attr) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_rwlock_init() failed"); } } @@ -69,6 +69,7 @@ int RWLock::trylock() { } RWLock::~RWLock() { + pthread_rwlockattr_destroy(&impl->attr); pthread_rwlock_destroy(&impl->_lock); if (shared_) { sw_mem_pool()->free(impl); diff --git a/src/lock/spin_lock.cc b/src/lock/spin_lock.cc index b0882ff6783..c073366d218 100644 --- a/src/lock/spin_lock.cc +++ b/src/lock/spin_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -34,7 +34,7 @@ SpinLock::SpinLock(int use_in_process) : Lock() { } type_ = SPIN_LOCK; - if (pthread_spin_init(impl, use_in_process) < 0) { + if (pthread_spin_init(impl, use_in_process) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_spin_init() failed"); } } diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index 661e01b9a65..a6df5959d8f 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,34 +19,34 @@ namespace swoole { +BufferChunk::BufferChunk(Type type, uint32_t size) : type(type), size(size) { + if (type == TYPE_DATA && size > 0) { + value.str = new char[size]; + } +} + +BufferChunk::~BufferChunk() { + if (type == TYPE_DATA) { + delete[] value.str; + } + if (destroy) { + destroy(this); + } +} + Buffer::Buffer(uint32_t _chunk_size) { chunk_size = _chunk_size == 0 ? INT_MAX : _chunk_size; } BufferChunk *Buffer::alloc(BufferChunk::Type type, uint32_t size) { - BufferChunk *chunk = new BufferChunk(); - - if (type == BufferChunk::TYPE_DATA && size > 0) { - chunk->size = size; - chunk->value.ptr = new char[size]; - } - - chunk->type = type; + auto *chunk = new BufferChunk(type, size); queue_.push(chunk); - return chunk; } void Buffer::pop() { BufferChunk *chunk = queue_.front(); - total_length -= chunk->size; - if (chunk->type == BufferChunk::TYPE_DATA) { - delete[] chunk->value.ptr; - } - if (chunk->destroy) { - chunk->destroy(chunk); - } delete chunk; queue_.pop(); } @@ -72,7 +72,7 @@ void Buffer::append(const void *data, uint32_t size) { total_length += _n; - memcpy(chunk->value.ptr, _pos, _n); + memcpy(chunk->value.str, _pos, _n); chunk->length = _n; swoole_trace_log(SW_TRACE_BUFFER, "chunk_n=%lu|size=%u|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); @@ -111,9 +111,9 @@ void Buffer::append(const struct iovec *iov, size_t iovcnt, off_t offset) { i++; continue; } else { + pos = (char *) iov[i].iov_base + offset; + iov_remain_len = iov[i].iov_len - offset; offset = 0; - pos += offset; - iov_remain_len -= offset; } } chunk_remain_len = _length >= chunk_size ? chunk_size : _length; @@ -121,7 +121,7 @@ void Buffer::append(const struct iovec *iov, size_t iovcnt, off_t offset) { } size_t _n = std::min(iov_remain_len, chunk_remain_len); - memcpy(chunk->value.ptr + chunk->length, pos, _n); + memcpy(chunk->value.str + chunk->length, pos, _n); total_length += _n; _length -= _n; diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index fdb89aef055..7e7daa4d4ef 100644 --- a/src/memory/fixed_pool.cc +++ b/src/memory/fixed_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,7 +22,7 @@ namespace swoole { struct FixedPoolSlice { uint8_t lock; FixedPoolSlice *next; - FixedPoolSlice *pre; + FixedPoolSlice *prev; char data[0]; }; @@ -79,8 +79,8 @@ FixedPool::FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared) { * create new FixedPool, Using the given memory */ FixedPool::FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared) { - impl = (FixedPoolImpl*) memory; - memory = (char*) memory + sizeof(*impl); + impl = (FixedPoolImpl *) memory; + memory = (char *) memory + sizeof(*impl); sw_memset_zero(impl, sizeof(*impl)); impl->shared = shared; impl->slice_size = slice_size; @@ -115,7 +115,7 @@ void FixedPoolImpl::init() { sw_memset_zero(slice, sizeof(FixedPoolSlice)); if (head != nullptr) { - head->pre = slice; + head->prev = slice; slice->next = head; } else { tail = slice; @@ -125,9 +125,9 @@ void FixedPoolImpl::init() { cur = (char *) cur + (sizeof(FixedPoolSlice) + slice_size); if (cur < max) { - slice->pre = (FixedPoolSlice *) cur; + slice->prev = (FixedPoolSlice *) cur; } else { - slice->pre = nullptr; + slice->prev = nullptr; break; } @@ -147,64 +147,54 @@ uint32_t FixedPool::get_slice_size() { } void *FixedPool::alloc(uint32_t size) { - FixedPoolSlice *slice; - - slice = impl->head; - - if (slice->lock == 0) { - slice->lock = 1; - impl->slice_use++; - /** - * move next slice to head (idle list) - */ - impl->head = slice->next; - slice->next->pre = nullptr; - - /* - * move this slice to tail (busy list) - */ - impl->tail->next = slice; - slice->next = nullptr; - slice->pre = impl->tail; - impl->tail = slice; - - return slice->data; - } else { + FixedPoolSlice *slice = impl->head; + if (slice->lock) { + swoole_set_last_error(SW_ERROR_MALLOC_FAIL); + assert(get_number_of_spare_slice() == 0); return nullptr; } + + slice->lock = 1; + impl->slice_use++; + + // move next slice to head (idle list) + impl->head = slice->next; + impl->head->prev = nullptr; + + // move this slice to tail (busy list) + impl->tail->next = slice; + slice->next = nullptr; + slice->prev = impl->tail; + impl->tail = slice; + + return slice->data; } void FixedPool::free(void *ptr) { - FixedPoolSlice *slice; + FixedPoolSlice *slice = (FixedPoolSlice *) ((char *) ptr - sizeof(FixedPoolSlice)); assert(ptr > impl->memory && (char *) ptr < (char *) impl->memory + impl->size); + assert(slice->lock == 1); - slice = (FixedPoolSlice *) ((char *) ptr - sizeof(FixedPoolSlice)); - - if (slice->lock) { - impl->slice_use--; - } - + impl->slice_use--; slice->lock = 0; - // list head, AB - if (slice->pre == nullptr) { + if (slice == impl->head) { return; } - // list tail, DE - if (slice->next == nullptr) { - slice->pre->next = nullptr; - impl->tail = slice->pre; - } - // middle BCD - else { - slice->pre->next = slice->next; - slice->next->pre = slice->pre; + + if (slice == impl->tail) { + slice->prev->next = nullptr; + impl->tail = slice->prev; + } else { + slice->prev->next = slice->next; + slice->next->prev = slice->prev; } - slice->pre = nullptr; + // move slice to head (idle) + slice->prev = nullptr; slice->next = impl->head; - impl->head->pre = slice; + impl->head->prev = slice; impl->head = slice; } @@ -219,7 +209,7 @@ FixedPool::~FixedPool() { } } -void FixedPool::debug() { +void FixedPool::debug(int max_lines) { int line = 0; FixedPoolSlice *slice = impl->head; @@ -230,16 +220,17 @@ void FixedPool::debug() { } printf("#%d\t", line); - printf("Slab[%p]\t", slice); - printf("pre=%p\t", slice->pre); + printf("slice[%p]\t", slice); + printf("prev=%p\t", slice->prev); printf("next=%p\t", slice->next); printf("tag=%d\t", slice->lock); printf("data=%p\n", slice->data); slice = slice->next; - line++; - if (line > 100) break; + if (line++ > max_lines) { + break; + } } } -} +} // namespace swoole diff --git a/src/memory/global_memory.cc b/src/memory/global_memory.cc index 682d7cc7417..d03837ea059 100644 --- a/src/memory/global_memory.cc +++ b/src/memory/global_memory.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -54,7 +54,7 @@ GlobalMemory::GlobalMemory(uint32_t pagesize, bool shared) { GlobalMemoryImpl::GlobalMemoryImpl(uint32_t _pagesize, bool _shared) { shared = _shared; - pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, SwooleG.pagesize); + pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, swoole_pagesize()); create_pid = SwooleG.pid; if (new_page() == nullptr) { diff --git a/src/memory/ring_buffer.cc b/src/memory/ring_buffer.cc index ce490492eb3..b53a3cae271 100644 --- a/src/memory/ring_buffer.cc +++ b/src/memory/ring_buffer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/shared_memory.cc b/src/memory/shared_memory.cc index c1d2f2b2b62..a93873effd6 100644 --- a/src/memory/shared_memory.cc +++ b/src/memory/shared_memory.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/table.cc b/src/memory/table.cc index 224748b52fb..c09dc3e8dc1 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -35,7 +35,7 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { conflict_proportion = SW_TABLE_CONFLICT_PROPORTION; } - Table *table = (Table *) sw_mem_pool()->alloc(sizeof(*table)); + auto table = (Table *) sw_mem_pool()->alloc(sizeof(Table)); if (table == nullptr) { return nullptr; } @@ -55,22 +55,13 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { return table; } -void Table::free() { - delete mutex; - if (iterator) { - delete iterator; - } - delete column_map; - delete column_list; -} - bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, size_t _size) { if (_type < TableColumn::TYPE_INT || _type > TableColumn::TYPE_STRING) { swoole_warning("unknown column type"); return false; } - TableColumn *col = new TableColumn(_name, _type, _size); + auto col = new TableColumn(_name, _type, _size); col->index = item_size; item_size += col->size; column_map->emplace(_name, col); @@ -79,10 +70,7 @@ bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, s return true; } -size_t Table::get_memory_size() { - if (memory_size > 0) { - return memory_size; - } +size_t Table::calc_memory_size() const { /** * table size + conflict size */ @@ -108,13 +96,15 @@ size_t Table::get_memory_size() { */ _memory_size += size * sizeof(TableRow *); - memory_size = _memory_size; - swoole_trace("_memory_size=%lu, _row_num=%lu, _row_memory_size=%lu", _memory_size, _row_num, _row_memory_size); return _memory_size; } +size_t Table::get_memory_size() const { + return memory_size; +} + uint32_t Table::get_available_slice_num() { lock(); uint32_t num = pool->get_number_of_spare_slice(); @@ -131,7 +121,7 @@ bool Table::create() { return false; } - size_t _memory_size = get_memory_size(); + size_t _memory_size = calc_memory_size(); size_t _row_memory_size = sizeof(TableRow) + item_size; void *_memory = sw_shm_malloc(_memory_size); @@ -153,6 +143,7 @@ bool Table::create() { _memory_size -= _row_memory_size * size; pool = new FixedPool(_row_memory_size, _memory, _memory_size, true); iterator = new TableIterator(_row_memory_size); + memory_size = _memory_size; created = true; return true; @@ -174,9 +165,7 @@ void Table::destroy() { } delete column_map; delete column_list; - if (iterator) { - delete iterator; - } + delete iterator; delete pool; if (memory) { sw_shm_free(memory); @@ -315,13 +304,11 @@ TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int * if (sw_mem_equal(row->key, row->key_len, key, keylen)) { break; } else if (row->next == nullptr) { - lock(); - TableRow *new_row = (TableRow *) pool->alloc(0); conflict_count++; if (_conflict_level > conflict_max_level) { conflict_max_level = _conflict_level; } - unlock(); + TableRow *new_row = alloc_row(); if (!new_row) { return nullptr; } @@ -398,14 +385,10 @@ bool Table::del(const char *key, uint16_t keylen) { memcpy(row->key, tmp->key, tmp->key_len + 1); row->key_len = tmp->key_len; memcpy(row->data, tmp->data, item_size); - } - if (prev) { + } else { prev->next = tmp->next; } - lock(); - tmp->clear(); - pool->free(tmp); - unlock(); + free_row(tmp); } _delete_element: diff --git a/src/network/address.cc b/src/network/address.cc index c608867c8d8..4a97dc27793 100644 --- a/src/network/address.cc +++ b/src/network/address.cc @@ -10,12 +10,14 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_socket.h" +#include + namespace swoole { namespace network { @@ -74,5 +76,40 @@ bool Address::assign(SocketType _type, const std::string &_host, int _port) { return false; } +bool Address::assign(const std::string &url) { + std::regex pattern(R"((tcp|udp)://([\[\]a-zA-Z0-9.-:]+):(\d+))"); + std::smatch match; + + if (std::regex_match(url, match, pattern)) { + std::string host = match[2]; + auto port = std::stoi(match[3]); + + if (host[0] == '[') { + type = SW_SOCK_TCP6; + addr.inet_v6.sin6_family = AF_INET6; + addr.inet_v6.sin6_port = htons(port); + len = sizeof(addr.inet_v6); + if (inet_pton(AF_INET6, host.substr(1, host.size() - 2).c_str(), addr.inet_v6.sin6_addr.s6_addr)) { + return true; + } + } else { + type = SW_SOCK_TCP; + addr.inet_v4.sin_family = AF_INET; + addr.inet_v4.sin_port = htons(port); + len = sizeof(addr.inet_v4); + if (!inet_pton(AF_INET, host.c_str(), &addr.inet_v4.sin_addr.s_addr)) { + if (gethostbyname(AF_INET, host.c_str(), (char *) &addr.inet_v4.sin_addr.s_addr) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return false; + } + } + return true; + } + } + + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_BAD_HOST_ADDR, "Invalid address['%s']", url.c_str()); + return false; +} + } // namespace network } // namespace swoole diff --git a/src/network/client.cc b/src/network/client.cc index 55bbe3a7c15..4620fb43915 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -51,7 +51,7 @@ static int Client_onWrite(Reactor *reactor, Event *event); static int Client_onError(Reactor *reactor, Event *event); static void Client_onTimeout(Timer *timer, TimerNode *tnode); static void Client_onResolveCompleted(AsyncEvent *event); -static int Client_onPackage(Protocol *proto, Socket *conn, const char *data, uint32_t length); +static int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata); static sw_inline void execute_onConnect(Client *cli) { if (cli->timer) { @@ -193,7 +193,7 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { ctx->state = SW_SOCKS5_STATE_AUTH; - return send(this, ctx->buf, ctx->username.length() + ctx->password.length() + 3, 0); + return send(this, ctx->buf, ctx->username.length() + ctx->password.length() + 3, 0) > 0 ? SW_OK : SW_ERR; } // send connect request else { @@ -211,14 +211,14 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { memcpy(buf, ctx->target_host.c_str(), ctx->target_host.length()); buf += ctx->target_host.length(); *(uint16_t *) buf = htons(ctx->target_port); - return send(this, ctx->buf, ctx->target_host.length() + 7, 0); + return send(this, ctx->buf, ctx->target_host.length() + 7, 0) > 0 ? SW_OK : SW_ERR; } else { buf[3] = 0x01; buf += 4; *(uint32_t *) buf = htons(ctx->target_host.length()); buf += 4; *(uint16_t *) buf = htons(ctx->target_port); - return send(this, ctx->buf, ctx->target_host.length() + 7, 0); + return send(this, ctx->buf, ctx->target_host.length() + 7, 0) > 0 ? SW_OK : SW_ERR; } } } else if (ctx->state == SW_SOCKS5_STATE_AUTH) { @@ -249,13 +249,14 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { #endif if (result == 0) { ctx->state = SW_SOCKS5_STATE_READY; + return SW_OK; } else { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_SERVER_ERROR, "Socks5 server error, reason :%s", Socks5Proxy::strerror(result)); + return SW_ERR; } - return result; } return SW_OK; } @@ -557,7 +558,6 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub if (ret >= 0) { cli->active = 1; - // socks5 proxy if (cli->socks5_proxy) { char buf[1024]; Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0x00 : 0x02); @@ -579,6 +579,21 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub } return SW_ERR; } + } else if (cli->http_proxy) { + auto proxy_buf = sw_tg_buffer(); + const std::string *host_name = cli->get_http_proxy_host_name(); + size_t n_write = cli->http_proxy->pack(proxy_buf, host_name); + if (cli->send(cli, proxy_buf->str, n_write, 0) < 0) { + return SW_ERR; + } + ssize_t n_read = cli->recv(cli, proxy_buf->str, proxy_buf->size, 0); + if (n_read <= 0) { + return SW_ERR; + } + proxy_buf->length = n_read; + if (!cli->http_proxy->handshake(proxy_buf)) { + return SW_ERR; + } } #ifdef SW_USE_OPENSSL @@ -615,30 +630,13 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou if (cli->wait_dns) { AsyncEvent ev{}; - - size_t len = strlen(cli->server_host); - if (len < SW_IP_MAX_LENGTH) { - ev.nbytes = SW_IP_MAX_LENGTH; - } else { - ev.nbytes = len + 1; - } - - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) { - swoole_warning("malloc failed"); - return SW_ERR; - } - - memcpy(ev.buf, cli->server_host, len); - ((char *) ev.buf)[len] = 0; - ev.flags = cli->_sock_domain; + auto req = new GethostbynameRequest(cli->server_host, cli->_sock_domain); + ev.data = std::shared_ptr(req); ev.object = cli; - ev.fd = cli->socket->fd; ev.handler = async::handler_gethostbyname; ev.callback = Client_onResolveCompleted; if (swoole::async::dispatch(&ev) == nullptr) { - sw_free(ev.buf); return SW_ERR; } else { return SW_OK; @@ -661,7 +659,7 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou return SW_ERR; } if (timeout > 0) { - cli->timer = swoole_timer_add((long) (timeout * 1000), false, Client_onTimeout, cli); + cli->timer = swoole_timer_add(timeout, false, Client_onTimeout, cli); } return SW_OK; } else { @@ -773,7 +771,7 @@ static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, in } } #ifdef SW_USE_OPENSSL - if (cli->socket->catch_error(errno) == SW_WAIT && cli->socket->ssl) { + if (cli->socket->catch_read_error(errno) == SW_WAIT && cli->socket->ssl) { int timeout_ms = (int) (cli->timeout * 1000); if (cli->socket->ssl_want_read && cli->socket->wait_event(timeout_ms, SW_EVENT_READ) == SW_OK) { continue; @@ -896,51 +894,9 @@ static ssize_t Client_udp_recv(Client *cli, char *data, size_t length, int flags return ret; } -#ifdef SW_USE_OPENSSL -static int Client_https_proxy_handshake(Client *cli) { - char *buf = cli->buffer->str; - size_t len = cli->buffer->length; - int state = 0; - char *p = buf; - char *pe = buf + len; - for (; p < pe; p++) { - if (state == 0) { - if (SW_STRCASECT(p, pe - p, "HTTP/1.1") || SW_STRCASECT(p, pe - p, "HTTP/1.0")) { - state = 1; - p += sizeof("HTTP/1.x") - 1; - } else { - break; - } - } else if (state == 1) { - if (isspace(*p)) { - continue; - } else { - if (SW_STRCASECT(p, pe - p, "200")) { - state = 2; - p += sizeof("200") - 1; - } else { - break; - } - } - } else if (state == 2) { - if (isspace(*p)) { - continue; - } else { - if (SW_STRCASECT(p, pe - p, "Connection established")) { - return SW_OK; - } else { - break; - } - } - } - } - return SW_ERR; -} -#endif - -static int Client_onPackage(Protocol *proto, Socket *conn, const char *data, uint32_t length) { +static int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata) { Client *cli = (Client *) conn->object; - cli->onReceive(cli, data, length); + cli->onReceive(cli, rdata->data, rdata->info.len); return conn->close_wait ? SW_ERR : SW_OK; } @@ -949,73 +905,57 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { Client *cli = (Client *) event->socket->object; char *buf = cli->buffer->str + cli->buffer->length; ssize_t buf_size = cli->buffer->size - cli->buffer->length; +#ifdef SW_USE_OPENSSL + bool do_ssl_handshake = cli->open_ssl; +#else + bool do_ssl_handshake = false; +#endif if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) { -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - n = event->socket->recv(buf, buf_size, 0); - if (n <= 0) { - goto __close; - } - cli->buffer->length += n; - if (cli->buffer->length < sizeof(SW_HTTPS_PROXY_HANDSHAKE_RESPONSE) - 1) { - return SW_OK; - } - if (Client_https_proxy_handshake(cli) < 0) { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); - goto _connect_fail; - } else { - cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; - cli->buffer->clear(); - } - if (cli->ssl_handshake() < 0) { - goto _connect_fail; - } else { - if (cli->socket->ssl_state == SW_SSL_STATE_READY) { - execute_onConnect(cli); - } else if (cli->socket->ssl_state == SW_SSL_STATE_WAIT_STREAM && cli->socket->ssl_want_write) { - swoole_event_set(event->socket, SW_EVENT_WRITE); - } + n = event->socket->recv(buf, buf_size, 0); + if (n <= 0) { + _connect_fail: + cli->active = 0; + cli->close(); + if (cli->onError) { + cli->onError(cli); } return SW_OK; } -#endif + cli->buffer->length += n; + if (!cli->http_proxy->handshake(cli->buffer)) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); + goto _connect_fail; + } + cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; + cli->buffer->clear(); + if (!do_ssl_handshake) { + execute_onConnect(cli); + return SW_OK; + } } + if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { n = event->socket->recv(buf, buf_size, 0); if (n <= 0) { - goto __close; + goto _connect_fail; } + cli->buffer->length += n; if (cli->socks5_handshake(buf, buf_size) < 0) { - goto __close; + goto _connect_fail; } if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { return SW_OK; } -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - if (cli->ssl_handshake() < 0) { - _connect_fail: - cli->active = 0; - cli->close(); - if (cli->onError) { - cli->onError(cli); - } - } else { - cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; - return swoole_event_set(event->socket, SW_EVENT_WRITE); - } - } else -#endif - { + cli->buffer->clear(); + if (!do_ssl_handshake) { execute_onConnect(cli); + return SW_OK; } - return SW_OK; } #ifdef SW_USE_OPENSSL - if (cli->open_ssl && cli->socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) { + if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) { if (cli->ssl_handshake() < 0) { goto _connect_fail; } @@ -1052,12 +992,9 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { } } -#ifdef SW_CLIENT_RECV_AGAIN -_recv_again: -#endif n = event->socket->recv(buf, buf_size, 0); if (n < 0) { - switch (event->socket->catch_error(errno)) { + switch (event->socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("Read from socket[%d] failed", event->fd); return SW_OK; @@ -1073,11 +1010,6 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { return cli->close(); } else { cli->onReceive(cli, buf, n); -#ifdef SW_CLIENT_RECV_AGAIN - if (n == buf_size) { - goto _recv_again; - } -#endif return SW_OK; } return SW_OK; @@ -1128,16 +1060,13 @@ static void Client_onTimeout(Timer *timer, TimerNode *tnode) { } static void Client_onResolveCompleted(AsyncEvent *event) { - if (event->canceled) { - sw_free(event->buf); - return; - } + GethostbynameRequest *req = dynamic_cast(event->data.get()); Client *cli = (Client *) event->object; cli->wait_dns = 0; if (event->error == 0) { - Client_tcp_connect_async(cli, (char *) event->buf, cli->server_port, cli->timeout, 1); + Client_tcp_connect_async(cli, req->addr, cli->server_port, cli->timeout, 1); } else { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); cli->socket->removed = 1; @@ -1146,7 +1075,6 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - sw_free(event->buf); } static int Client_onWrite(Reactor *reactor, Event *event) { @@ -1196,23 +1124,17 @@ static int Client_onWrite(Reactor *reactor, Event *event) { // socks5 proxy if (cli->socks5_proxy && cli->socks5_proxy->state == SW_SOCKS5_STATE_WAIT) { char buf[3]; - Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0x00 : 0x02); + Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0 : SW_SOCKS5_METHOD_AUTH); cli->socks5_proxy->state = SW_SOCKS5_STATE_HANDSHAKE; return cli->send(cli, buf, sizeof(buf), 0); } // http proxy if (cli->http_proxy && cli->http_proxy->state == SW_HTTP_PROXY_STATE_WAIT) { -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - cli->http_proxy->state = SW_HTTP_PROXY_STATE_HANDSHAKE; - int n = sw_snprintf(cli->http_proxy->buf, - sizeof(cli->http_proxy->buf), - "CONNECT %s:%d HTTP/1.1\r\n\r\n", - cli->http_proxy->target_host.c_str(), - cli->http_proxy->target_port); - return cli->send(cli, cli->http_proxy->buf, n, 0); - } -#endif + auto proxy_buf = sw_tg_buffer(); + const std::string *host_name = cli->get_http_proxy_host_name(); + size_t n = cli->http_proxy->pack(proxy_buf, host_name); + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + return cli->send(cli, proxy_buf->str, n, 0); } #ifdef SW_USE_OPENSSL if (cli->open_ssl) { diff --git a/src/network/dns.cc b/src/network/dns.cc index da5531bf269..22ac3ab9dd8 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -10,12 +10,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" #include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" #include "swoole_util.h" #include @@ -31,7 +32,10 @@ #include #endif -bool swoole_load_resolv_conf() { +using swoole::NameResolver; +using swoole::coroutine::System; + +SW_API bool swoole_load_resolv_conf() { FILE *fp; char line[100]; char buf[16] = {}; @@ -57,10 +61,65 @@ bool swoole_load_resolv_conf() { return true; } -void swoole_set_hosts_path(const std::string &hosts_file) { +SW_API void swoole_set_hosts_path(const std::string &hosts_file) { SwooleG.dns_hosts_path = hosts_file; } +SW_API void swoole_name_resolver_add(const NameResolver &resolver, bool append) { + if (append) { + SwooleG.name_resolvers.push_back(resolver); + } else { + SwooleG.name_resolvers.push_front(resolver); + } +} + +SW_API void swoole_name_resolver_each( + const std::function::iterator &iter)> &fn) { + for (auto iter = SwooleG.name_resolvers.begin(); iter != SwooleG.name_resolvers.end(); iter++) { + enum swTraverseOperation op = fn(iter); + switch (op) { + case SW_TRAVERSE_REMOVE: + SwooleG.name_resolvers.erase(iter++); + continue; + case SW_TRAVERSE_STOP: + break; + default: + case SW_TRAVERSE_KEEP: + continue; + } + } +} + +SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, NameResolver::Context *ctx) { + if (SwooleG.name_resolvers.empty()) { + goto _dns_lookup; + } + for (auto iter = SwooleG.name_resolvers.begin(); iter != SwooleG.name_resolvers.end(); iter++) { + std::string result = iter->resolve(host_name, ctx, iter->private_data); + if (!result.empty() || ctx->final_) { + return result; + } + } +_dns_lookup: + /* + * Use DNS to resolve host name by default + */ + if (swoole_coroutine_is_in()) { + return System::gethostbyname(host_name, ctx->type, ctx->timeout); + } else { + char addr[INET6_ADDRSTRLEN] = {}; + if (swoole::network::gethostbyname(ctx->type, host_name.c_str(), sw_tg_buffer()->str) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return ""; + } + if (!inet_ntop(ctx->type, sw_tg_buffer()->str, addr, sizeof(addr))) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return ""; + } + return std::string(addr); + } +} + namespace swoole { namespace coroutine { @@ -285,7 +344,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -314,7 +373,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -708,36 +767,40 @@ int getaddrinfo(GetaddrinfoRequest *req) { hints.ai_socktype = req->socktype; hints.ai_protocol = req->protocol; - int ret = ::getaddrinfo(req->hostname, req->service, &hints, &result); + int ret = ::getaddrinfo(req->hostname.c_str(), req->service.c_str(), &hints, &result); if (ret != 0) { req->error = ret; return SW_ERR; } - void *buffer = req->result; int i = 0; - for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) { + for (ptr = result; ptr != nullptr; ptr = ptr->ai_next, i++) { + } + req->count = SW_MIN(i, SW_DNS_HOST_BUFFER_SIZE); + req->results.resize(req->count); + + for (ptr = result, i = 0; ptr != nullptr; ptr = ptr->ai_next, i++) { switch (ptr->ai_family) { case AF_INET: - memcpy((char *) buffer + (i * sizeof(struct sockaddr_in)), ptr->ai_addr, sizeof(struct sockaddr_in)); + memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in)); break; case AF_INET6: - memcpy((char *) buffer + (i * sizeof(struct sockaddr_in6)), ptr->ai_addr, sizeof(struct sockaddr_in6)); + memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in6)); break; default: swoole_warning("unknown socket family[%d]", ptr->ai_family); break; } - i++; if (i == SW_DNS_HOST_BUFFER_SIZE) { break; } } ::freeaddrinfo(result); req->error = 0; - req->count = i; + return SW_OK; } +} // namespace network void GetaddrinfoRequest::parse_result(std::vector &retval) { struct sockaddr_in *addr_v4; @@ -746,12 +809,12 @@ void GetaddrinfoRequest::parse_result(std::vector &retval) { char tmp[INET6_ADDRSTRLEN]; const char *r; - for (int i = 0; i < count; i++) { + for (auto &addr : results) { if (family == AF_INET) { - addr_v4 = (struct sockaddr_in *) ((char *) result + (i * sizeof(struct sockaddr_in))); + addr_v4 = (struct sockaddr_in *) &addr; r = inet_ntop(AF_INET, (const void *) &addr_v4->sin_addr, tmp, sizeof(tmp)); } else { - addr_v6 = (struct sockaddr_in6 *) ((char *) result + (i * sizeof(struct sockaddr_in6))); + addr_v6 = (struct sockaddr_in6 *) &addr; r = inet_ntop(AF_INET6, (const void *) &addr_v6->sin6_addr, tmp, sizeof(tmp)); } if (r) { @@ -759,5 +822,4 @@ void GetaddrinfoRequest::parse_result(std::vector &retval) { } } } -} // namespace network } // namespace swoole diff --git a/src/network/socket.cc b/src/network/socket.cc index 6380e0b8b5b..791195441dc 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -73,7 +73,7 @@ void IOVector::update_iterator(ssize_t __n) { return; } iov_iterator += _index; - iov_iterator->iov_base = reinterpret_cast (iov_iterator->iov_base) + _offset_bytes; + iov_iterator->iov_base = reinterpret_cast(iov_iterator->iov_base) + _offset_bytes; iov_iterator->iov_len = iov_iterator->iov_len - _offset_bytes; return; @@ -103,19 +103,16 @@ int Socket::sendfile_blocking(const char *filename, off_t offset, size_t length, length = offset + length; } - int n, sendn; + ssize_t n, sent_bytes; while (offset < (off_t) length) { if (wait_event(timeout_ms, SW_EVENT_WRITE) < 0) { return SW_ERR; - } else { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; - n = ::swoole_sendfile(fd, file.get_fd(), &offset, sendn); - if (n <= 0) { - swoole_sys_warning("sendfile(%d, %s) failed", fd, filename); - return SW_ERR; - } else { - continue; - } + } + sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; + n = ::swoole_sendfile(fd, file.get_fd(), &offset, sent_bytes); + if (n <= 0) { + swoole_sys_warning("sendfile(%d, %s) failed", fd, filename); + return SW_ERR; } } return SW_OK; @@ -127,7 +124,7 @@ ssize_t Socket::writev_blocking(const struct iovec *iov, size_t iovcnt) { if (n < 0) { if (errno == EINTR) { continue; - } else if (catch_error(errno) == SW_WAIT && + } else if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } else { @@ -173,9 +170,14 @@ int Socket::wait_event(int timeout_ms, int events) { if (ret == 0) { swoole_set_last_error(SW_ERROR_SOCKET_POLL_TIMEOUT); return SW_ERR; - } else if (ret < 0 && errno != EINTR) { - swoole_sys_warning("poll() failed"); - return SW_ERR; + } else if (ret < 0) { + if (errno != EINTR) { + swoole_sys_warning("poll() failed"); + } else if (dont_restart) { + return SW_ERR; + } else { + continue; + } } else { return SW_OK; } @@ -199,7 +201,7 @@ ssize_t Socket::send_blocking(const void *__data, size_t __len) { if (n < 0) { if (errno == EINTR) { continue; - } else if (catch_error(errno) == SW_WAIT && + } else if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } else { @@ -228,7 +230,8 @@ ssize_t Socket::recv_blocking(void *__data, size_t __len, int flags) { if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + if (catch_read_error(errno) == SW_WAIT && + wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { continue; } return ret; @@ -276,7 +279,7 @@ ssize_t Socket::sendto_blocking(const Address &sa, const void *__buf, size_t __n if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { + if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } break; @@ -296,7 +299,7 @@ ssize_t Socket::recvfrom_blocking(char *__buf, size_t __len, int flags, Address if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + if (catch_read_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { continue; } break; @@ -417,7 +420,7 @@ bool Socket::set_buffer_size(uint32_t _buffer_size) { bool Socket::set_recv_buffer_size(uint32_t _buffer_size) { if (set_option(SOL_SOCKET, SO_RCVBUF, _buffer_size) != 0) { - swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, _buffer_size); + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, _buffer_size); return false; } return true; @@ -425,7 +428,7 @@ bool Socket::set_recv_buffer_size(uint32_t _buffer_size) { bool Socket::set_send_buffer_size(uint32_t _buffer_size) { if (set_option(SOL_SOCKET, SO_SNDBUF, _buffer_size) != 0) { - swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, _buffer_size); + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, _buffer_size); return false; } return true; @@ -435,6 +438,13 @@ bool Socket::set_timeout(double timeout) { return set_recv_timeout(timeout) and set_send_timeout(timeout); } +Socket *Socket::dup() { + Socket *_socket = new Socket(); + *_socket = *this; + _socket->fd = ::dup(fd); + return _socket; +} + static bool _set_timeout(int fd, int type, double timeout) { int ret; struct timeval timeo; @@ -539,7 +549,7 @@ int Socket::handle_sendfile() { int ret; Buffer *buffer = out_buffer; BufferChunk *chunk = buffer->front(); - SendfileRequest *task = (SendfileRequest *) chunk->value.object; + SendfileRequest *task = (SendfileRequest *) chunk->value.ptr; if (task->offset == 0) { cork(); @@ -560,9 +570,10 @@ int Socket::handle_sendfile() { swoole_trace("ret=%d|task->offset=%ld|sendn=%lu|filesize=%lu", ret, (long) task->offset, sendn, task->length); if (ret <= 0) { - switch (catch_error(errno)) { + switch (catch_write_error(errno)) { case SW_ERROR: - swoole_sys_warning("sendfile(%s, %ld, %zu) failed", task->file.get_path().c_str(), (long) task->offset, sendn); + swoole_sys_warning( + "sendfile(%s, %ld, %zu) failed", task->file.get_path().c_str(), (long) task->offset, sendn); buffer->pop(); return SW_OK; case SW_CLOSE: @@ -602,9 +613,9 @@ int Socket::handle_send() { return SW_OK; } - ssize_t ret = send(chunk->value.ptr + chunk->offset, sendn, 0); + ssize_t ret = send(chunk->value.str + chunk->offset, sendn, 0); if (ret < 0) { - switch (catch_error(errno)) { + switch (catch_write_error(errno)) { case SW_ERROR: swoole_sys_warning("send to fd[%d] failed", fd); break; @@ -634,7 +645,7 @@ int Socket::handle_send() { } static void Socket_sendfile_destructor(BufferChunk *chunk) { - SendfileRequest *task = (SendfileRequest *) chunk->value.object; + SendfileRequest *task = (SendfileRequest *) chunk->value.ptr; delete task; } @@ -674,7 +685,7 @@ int Socket::sendfile(const char *filename, off_t offset, size_t length) { } BufferChunk *chunk = out_buffer->alloc(BufferChunk::TYPE_SENDFILE, 0); - chunk->value.object = task.release(); + chunk->value.ptr = task.release(); chunk->destroy = Socket_sendfile_destructor; return SW_OK; @@ -716,7 +727,7 @@ ssize_t Socket::recv(void *__buf, size_t __n, int __flags) { } // The POLLHUP event is triggered, but Socket::recv returns EAGAIN - if (total_bytes < 0 && catch_error(errno) == SW_WAIT && event_hup) { + if (total_bytes < 0 && catch_read_error(errno) == SW_WAIT && event_hup) { total_bytes = 0; } @@ -759,6 +770,28 @@ ssize_t Socket::send_async(const void *__buf, size_t __n) { } } +ssize_t Socket::read_sync(void *__buf, size_t __len, int timeout_ms) { + struct pollfd event; + event.fd = fd; + event.events = POLLIN; + if (poll(&event, 1, timeout_ms) == 1) { + return read(__buf, __len); + } else { + return -1; + } +} + +ssize_t Socket::write_sync(const void *__buf, size_t __len, int timeout_ms) { + struct pollfd event; + event.fd = fd; + event.events = POLLOUT; + if (poll(&event, 1, timeout_ms) == 1) { + return write(__buf, __len); + } else { + return -1; + } +} + ssize_t Socket::readv(IOVector *io_vector) { ssize_t retval; @@ -781,7 +814,6 @@ ssize_t Socket::writev(IOVector *io_vector) { ssize_t retval; do { - #ifdef SW_USE_OPENSSL if (ssl) { retval = ssl_writev(io_vector); @@ -822,7 +854,7 @@ static int ssl_check_name(const char *name, ASN1_STRING *pattern) { char *s, *end; size_t slen, plen; - s = (char *)name; + s = (char *) name; slen = strlen(name); uchar *p = ASN1_STRING_data(pattern); @@ -978,7 +1010,7 @@ X509 *Socket::ssl_get_peer_certificate() { return SSL_get_peer_certificate(ssl); } -STACK_OF(X509) *Socket::ssl_get_peer_cert_chain() { +STACK_OF(X509) * Socket::ssl_get_peer_cert_chain() { if (!ssl) { return NULL; } @@ -1017,7 +1049,13 @@ std::vector Socket::ssl_get_peer_cert_chain(int limit) { return list; } auto n = sk_X509_num(chain); + +#ifdef OPENSSL_IS_BORINGSSL + n = std::min((int) n, limit); +#else n = std::min(n, limit); +#endif + SW_LOOP_N(n) { X509 *cert = sk_X509_value(chain, i); auto n = _ssl_read_x509_file(cert, sw_tg_buffer()->str, sw_tg_buffer()->size); @@ -1149,13 +1187,18 @@ int Socket::ssl_connect() { long err_code = ERR_get_error(); char *msg = ERR_error_string(err_code, sw_tg_buffer()->str); - swoole_notice("Socket::ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]", fd, info.get_ip(), info.get_port(), msg, - err, ERR_GET_REASON(err_code)); + swoole_notice("Socket::ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]", + fd, + info.get_ip(), + info.get_port(), + msg, + err, + ERR_GET_REASON(err_code)); return SW_ERR; } -int Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { +ssize_t Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { char buf[SW_BUFFER_SIZE_BIG]; ssize_t readn = _size > sizeof(buf) ? sizeof(buf) : _size; @@ -1163,7 +1206,7 @@ int Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { if (n > 0) { ssize_t ret = ssl_send(buf, n); if (ret < 0) { - if (catch_error(errno) == SW_ERROR) { + if (catch_write_error(errno) == SW_ERROR) { swoole_sys_warning("write() failed"); } } else { @@ -1326,8 +1369,7 @@ ssize_t Socket::ssl_recv(void *__buf, size_t __n) { return SW_ERR; case SSL_ERROR_SYSCALL: - errno = SW_ERROR_SSL_RESET; - return SW_ERR; + return errno == 0 ? 0 : SW_ERR; case SSL_ERROR_SSL: ssl_catch_error(); @@ -1426,6 +1468,11 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { swoole_warning("SSL_set_ex_data() failed"); return SW_ERR; } + +#ifdef OPENSSL_IS_BORINGSSL + SSL_set_enable_ech_grease(ssl, ssl_context->grease); +#endif + ssl_state = 0; return SW_OK; } @@ -1443,9 +1490,28 @@ Socket *make_socket(SocketType type, FdType fd_type, int flags) { if (Socket::get_domain_and_type(type, &sock_domain, &sock_type) < 0) { swoole_warning("unknown socket type [%d]", type); errno = ESOCKTNOSUPPORT; + swoole_set_last_error(errno); + return nullptr; + } + + return make_socket(type, fd_type, sock_domain, sock_type, 0, flags); +} + +Socket *make_socket(SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags) { + int sockfd = swoole::socket(sock_domain, sock_type, socket_protocol, flags); + if (sockfd < 0) { + swoole_set_last_error(errno); return nullptr; } + auto _socket = swoole::make_socket(sockfd, fd_type); + _socket->nonblock = !!(flags & SW_SOCK_NONBLOCK); + _socket->cloexec = !!(flags & SW_SOCK_CLOEXEC); + _socket->socket_type = type; + return _socket; +} + +int socket(int sock_domain, int sock_type, int socket_protocol, int flags) { bool nonblock = flags & SW_SOCK_NONBLOCK; bool cloexec = flags & SW_SOCK_CLOEXEC; @@ -1457,27 +1523,23 @@ Socket *make_socket(SocketType type, FdType fd_type, int flags) { if (cloexec) { sock_flags |= SOCK_CLOEXEC; } - int sockfd = socket(sock_domain, sock_type | sock_flags, 0); + int sockfd = ::socket(sock_domain, sock_type | sock_flags, socket_protocol); if (sockfd < 0) { - return nullptr; + return sockfd; } #else - int sockfd = socket(sock_domain, sock_type, 0); + int sockfd = ::socket(sock_domain, sock_type, socket_protocol); if (sockfd < 0) { - return nullptr; + return sockfd; } if (nonblock || cloexec) { if (!network::_fcntl_set_option(sockfd, nonblock ? 1 : -1, cloexec ? 1 : -1)) { close(sockfd); - return nullptr; + return sockfd; } } #endif - auto _socket = swoole::make_socket(sockfd, fd_type); - _socket->nonblock = nonblock; - _socket->cloexec = cloexec; - _socket->socket_type = type; - return _socket; + return sockfd; } Socket *make_server_socket(SocketType type, const char *address, int port, int backlog) { diff --git a/src/network/stream.cc b/src/network/stream.cc index 177ed4ef49a..189ee317eca 100644 --- a/src/network/stream.cc +++ b/src/network/stream.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -117,8 +117,10 @@ void Stream::set_max_length(uint32_t max_length) { } int Stream::send(const char *data, size_t length) { + assert(data != nullptr); + assert(length > 0); if (buffer == nullptr) { - buffer = new String(swoole_size_align(length + 4, SwooleG.pagesize)); + buffer = new String(swoole_size_align(length + 4, swoole_pagesize())); buffer->length = 4; } if (buffer->append(data, length) < 0) { diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index 16ae5c3e179..63f7dc90f51 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -31,6 +31,10 @@ #include #include #include +#include + +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; namespace swoole { namespace async { @@ -82,6 +86,10 @@ class ThreadPool { shutdown(); } + bool is_running() { + return running; + } + bool start() { running = true; current_task_id = 0; @@ -110,16 +118,14 @@ class ThreadPool { } delete _thread; } + threads.clear(); return true; } void schedule() { if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) { - event_mutex.lock(); double _max_wait_time = _queue.get_max_wait_time(); - event_mutex.unlock(); - if (_max_wait_time > max_wait_time) { size_t n = 1; /** @@ -129,10 +135,10 @@ class ThreadPool { n = worker_num - threads.size(); } swoole_trace_log(SW_TRACE_AIO, - "Create %zu thread due to wait %fs, we will have %zu threads", - n, - _max_wait_time, - threads.size() + n); + "Create %zu thread due to wait %fs, we will have %zu threads", + n, + _max_wait_time, + threads.size() + n); while (n--) { create_thread(); } @@ -141,14 +147,12 @@ class ThreadPool { } AsyncEvent *dispatch(const AsyncEvent *request) { - if (SwooleTG.async_threads->schedule) { - schedule(); - } auto _event_copy = new AsyncEvent(*request); + event_mutex.lock(); + schedule(); _event_copy->task_id = current_task_id++; _event_copy->timestamp = microtime(); _event_copy->pipe_socket = SwooleTG.async_threads->write_socket; - event_mutex.lock(); _queue.push(_event_copy); _cv.notify_one(); event_mutex.unlock(); @@ -179,9 +183,9 @@ class ThreadPool { } else { std::thread *_thread = i->second; swoole_trace_log(SW_TRACE_AIO, - "release idle thread#%s, we have %zu now", - get_thread_id(tid).c_str(), - threads.size() - 1); + "release idle thread#%s, we have %zu now", + get_thread_id(tid).c_str(), + threads.size() - 1); if (_thread->joinable()) { _thread->join(); } @@ -204,6 +208,7 @@ class ThreadPool { private: void create_thread(const bool is_core_worker = false); + void main_func(const bool is_core_worker); size_t core_worker_num; size_t worker_num; @@ -221,102 +226,99 @@ class ThreadPool { std::condition_variable _cv; }; -void ThreadPool::create_thread(const bool is_core_worker) { - try { - std::thread *_thread = new std::thread([this, is_core_worker]() { - bool exit_flag = false; - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; - - swoole_signal_block_all(); - - while (running) { - event_mutex.lock(); - AsyncEvent *event = _queue.pop(); - event_mutex.unlock(); - - swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); - - if (event) { - if (sw_unlikely(event->handler == nullptr)) { - event->error = SW_ERROR_AIO_BAD_REQUEST; - event->retval = -1; - } else if (sw_unlikely(event->canceled)) { - event->error = SW_ERROR_AIO_CANCELED; - event->retval = -1; - } else { - event->handler(event); - } +void ThreadPool::main_func(bool is_core_worker) { + bool exit_flag = false; + swoole_thread_init(); - swoole_trace_log(SW_TRACE_AIO, - "aio_thread %s. ret=%ld, error=%d", - event->retval > 0 ? "ok" : "failed", - event->retval, - event->error); - - _send_event: - while (true) { - ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); - if (ret < 0) { - if (errno == EAGAIN) { - event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); - continue; - } else if (errno == EINTR) { - continue; - } else { - delete event; - swoole_sys_warning("sendto swoole_aio_pipe_write failed"); - } - } - break; - } + while (running) { + event_mutex.lock(); + AsyncEvent *event = _queue.pop(); + event_mutex.unlock(); - // exit - if (exit_flag) { - n_closing--; - break; - } - } else { - std::unique_lock lock(event_mutex); - if (_queue.count() > 0) { + swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); + + if (event) { + if (sw_unlikely(event->handler == nullptr)) { + event->error = SW_ERROR_AIO_BAD_REQUEST; + event->retval = -1; + } else if (sw_unlikely(event->canceled)) { + event->error = SW_ERROR_AIO_CANCELED; + event->retval = -1; + } else { + event->handler(event); + } + + swoole_trace_log(SW_TRACE_AIO, + "aio_thread %s. ret=%ld, error=%d", + event->retval > 0 ? "ok" : "failed", + event->retval, + event->error); + + _send_event: + while (true) { + ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); + if (ret < 0) { + if (errno == EAGAIN) { + event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); + continue; + } else if (errno == EINTR) { continue; - } - if (!running) { - break; - } - ++n_waiting; - if (is_core_worker || max_idle_time <= 0) { - _cv.wait(lock); } else { - while (true) { - if (_cv.wait_for(lock, std::chrono::microseconds((size_t)(max_idle_time * 1000 * 1000))) == - std::cv_status::timeout) { - if (running && n_closing != 0) { - // wait for the next round - continue; - } - /* notifies the main thread to release this thread */ - event = new AsyncEvent; - event->object = new std::thread::id(std::this_thread::get_id()); - event->callback = release_callback; - event->pipe_socket = SwooleG.aio_default_socket; - event->canceled = false; - - --n_waiting; - ++n_closing; - exit_flag = true; - goto _send_event; - } - break; + delete event; + swoole_sys_warning("sendto swoole_aio_pipe_write failed"); + } + } + break; + } + + // exit + if (exit_flag) { + n_closing--; + break; + } + } else { + std::unique_lock lock(event_mutex); + if (_queue.count() > 0) { + continue; + } + if (!running) { + break; + } + ++n_waiting; + if (is_core_worker || max_idle_time <= 0) { + _cv.wait(lock); + } else { + while (true) { + if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == + std::cv_status::timeout) { + if (running && n_closing != 0) { + // wait for the next round + continue; } + /* notifies the main thread to release this thread */ + event = new AsyncEvent; + event->object = new std::thread::id(std::this_thread::get_id()); + event->callback = release_callback; + event->pipe_socket = SwooleG.aio_default_socket; + event->canceled = false; + + --n_waiting; + ++n_closing; + exit_flag = true; + goto _send_event; } - --n_waiting; + break; } } - }); + --n_waiting; + } + } + swoole_thread_clean(); +} + +void ThreadPool::create_thread(const bool is_core_worker) { + try { + std::thread *_thread = new std::thread([this, is_core_worker]() { main_func(is_core_worker); }); threads[_thread->get_id()] = _thread; } catch (const std::system_error &e) { swoole_sys_notice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); @@ -339,10 +341,6 @@ AsyncEvent *dispatch(const AsyncEvent *request) { } // namespace async int AsyncThreads::callback(Reactor *reactor, Event *event) { - if (SwooleTG.async_threads->schedule) { - SwooleTG.async_threads->pool->schedule(); - } - AsyncEvent *events[SW_AIO_EVENT_NUM]; ssize_t n = event->socket->read(events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM); if (n < 0) { @@ -411,20 +409,32 @@ AsyncThreads::AsyncThreads() { return true; }); - init_lock.lock(); - pool = new async::ThreadPool( - SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); - pool->start(); - schedule = true; - init_lock.unlock(); + async_thread_lock.lock(); + if (!async_thread_pool) { + async_thread_pool = std::make_shared( + SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); + } + if (!async_thread_pool->is_running()) { + async_thread_pool->start(); + } + pool = async_thread_pool; + async_thread_lock.unlock(); SwooleG.aio_default_socket = write_socket; SwooleTG.async_threads = this; } AsyncThreads::~AsyncThreads() { - delete pool; - pool = nullptr; + pool.reset(); + async_thread_lock.lock(); + /** + * When the reference count is 1, it means that all reactor threads have ended + * and all aio threads can be terminated. + */ + if (async_thread_pool.use_count() == 1) { + async_thread_pool->shutdown(); + } + async_thread_lock.unlock(); pipe->close(); read_socket = nullptr; write_socket = nullptr; diff --git a/src/os/base.cc b/src/os/base.cc index 1d1efc15543..94e8e7ad64f 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -78,20 +78,29 @@ int swoole_set_cpu_affinity(cpu_set_t *set) { return sched_setaffinity(getpid(), sizeof(*set), set); #endif } + +int swoole_get_cpu_affinity(cpu_set_t *set) { +#ifdef __FreeBSD__ + return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set); +#else + return sched_getaffinity(getpid(), sizeof(*set), set); +#endif +} #endif namespace swoole { namespace async { void handler_gethostbyname(AsyncEvent *event) { - char addr[SW_IP_MAX_LENGTH]; - int ret = network::gethostbyname(event->flags, (char *) event->buf, addr); - sw_memset_zero(event->buf, event->nbytes); + char addr[INET6_ADDRSTRLEN]; + auto req = dynamic_cast(event->data.get()); + int ret = network::gethostbyname(req->family, req->name.c_str(), addr); + sw_memset_zero(req->addr, req->addr_len); if (ret < 0) { event->error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; } else { - if (inet_ntop(event->flags, addr, (char *) event->buf, event->nbytes) == nullptr) { + if (inet_ntop(req->family, addr, req->addr, req->addr_len) == nullptr) { ret = -1; event->error = SW_ERROR_BAD_IPV6_ADDRESS; } else { @@ -103,7 +112,7 @@ void handler_gethostbyname(AsyncEvent *event) { } void handler_getaddrinfo(AsyncEvent *event) { - network::GetaddrinfoRequest *req = (network::GetaddrinfoRequest *) event->req; + auto req = dynamic_cast(event->data.get()); event->retval = network::getaddrinfo(req); event->error = req->error; } diff --git a/src/os/file.cc b/src/os/file.cc index 93f86fb5ff0..612145a50ff 100644 --- a/src/os/file.cc +++ b/src/os/file.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -113,6 +113,10 @@ bool file_put_contents(const std::string &filename, const char *content, size_t return file.write_all(content, length); } +bool file_exists(const std::string &filename) { + return access(filename.c_str(), F_OK) == 0; +} + size_t File::write_all(const void *data, size_t len) { size_t written_bytes = 0; while (written_bytes < len) { @@ -158,6 +162,25 @@ size_t File::read_all(void *buf, size_t len) { return read_bytes; } +ssize_t File::read_line(void *__buf, size_t __n) { + char *buf = (char *) __buf; + auto offset = get_offset(); + ssize_t read_bytes = read(buf, __n - 1); + if (read_bytes <= 0) { + return read_bytes; + } + for (ssize_t i = 0; i < read_bytes; ++i) { + if (buf[i] == '\0' || buf[i] == '\n') { + buf[i + 1] = '\0'; + set_offset(offset + i + 1); + return i + 1; + } + } + buf[read_bytes] = '\0'; + set_offset(offset + read_bytes + 1); + return read_bytes; +} + std::shared_ptr File::read_content() { ssize_t n = 0; std::shared_ptr data = std::make_shared(SW_BUFFER_SIZE_STD); diff --git a/src/os/msg_queue.cc b/src/os/msg_queue.cc index bf668a531d4..89d98448a07 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -86,7 +86,6 @@ bool MsgQueue::push(QueueNode *in, size_t mdata_length) { } swoole_set_last_error(errno); break; - } return false; } diff --git a/src/os/pipe.cc b/src/os/pipe.cc index 7a8ca544548..4a7dbf53499 100644 --- a/src/os/pipe.cc +++ b/src/os/pipe.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -59,7 +59,7 @@ ssize_t SocketPair::read(void *data, size_t length) { ssize_t SocketPair::write(const void *data, size_t length) { ssize_t n = master_socket->write(data, length); - if (blocking && n < 0 && timeout > 0 && master_socket->catch_error(errno) == SW_WAIT) { + if (blocking && n < 0 && timeout > 0 && master_socket->catch_write_error(errno) == SW_WAIT) { if (master_socket->wait_event(timeout * 1000, SW_EVENT_READ) < 0) { return SW_ERR; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 0cd02e70a80..634be9ac1ac 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -25,19 +25,23 @@ #include "swoole_process_pool.h" #include "swoole_client.h" +SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {}; + namespace swoole { using network::Socket; using network::Stream; -/** - * call onTask - */ -static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker); -/** - * call onMessage - */ -static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker); +static inline swReturnCode catch_system_error(int error) { + switch (error) { + case SW_SUCCESS: + case EAGAIN: + case EINTR: + return SW_CONTINUE; + default: + return SW_ERROR; + } +} void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { uint32_t i; @@ -78,7 +82,7 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip return SW_ERR; } - if (create_message_box(65536) < 0) { + if (create_message_box(SW_MESSAGE_BOX_SIZE) < 0) { return SW_ERR; } @@ -114,11 +118,11 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip } map_ = new std::unordered_map; - ipc_mode = _ipc_mode; - if (_ipc_mode > SW_IPC_NONE) { - main_loop = ProcessPool_worker_loop; - } + main_loop = run_with_task_protocol; + protocol_type_ = SW_PROTOCOL_TASK; + max_packet_size_ = SW_INPUT_BUFFER_SIZE; + max_wait_time = SW_WORKER_MAX_WAIT_TIME; SW_LOOP_N(_worker_num) { workers[i].pool = this; @@ -135,9 +139,47 @@ int ProcessPool::create_message_box(size_t memory_size) { return SW_OK; } +int ProcessPool::create_message_bus() { + if (ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + if (message_bus) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, "the message bus has been created"); + return SW_ERR; + } + sw_atomic_long_t *msg_id = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); + if (msg_id == nullptr) { + swoole_sys_warning("malloc[1] failed"); + return SW_ERR; + } + *msg_id = 1; + message_bus = new MessageBus(); + message_bus->set_id_generator([msg_id]() { return sw_atomic_fetch_add(msg_id, 1); }); + size_t ipc_max_size; +#ifndef __linux__ + ipc_max_size = SW_IPC_MAX_SIZE; +#else + int bufsize; + /** + * Get the maximum ipc[unix socket with dgram] transmission length + */ + if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { + bufsize = SW_IPC_MAX_SIZE; + } + ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE) - SW_DGRAM_HEADER_SIZE; +#endif + message_bus->set_buffer_size(ipc_max_size); + if (!message_bus->alloc_buffer()) { + return SW_ERR; + } + return SW_OK; +} + int ProcessPool::listen(const char *socket_file, int blacklog) { if (ipc_mode != SW_IPC_SOCKET) { - swoole_warning("ipc_mode is not SW_IPC_SOCKET"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_SOCKET"); return SW_ERR; } stream_info_->socket_file = sw_strdup(socket_file); @@ -169,26 +211,67 @@ int ProcessPool::listen(const char *host, int port, int blacklog) { return SW_OK; } -/** - * start workers - */ -int ProcessPool::start() { +void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { + switch (_protocol_type) { + case SW_PROTOCOL_TASK: + main_loop = run_with_task_protocol; + break; + case SW_PROTOCOL_STREAM: + main_loop = run_with_stream_protocol; + break; + case SW_PROTOCOL_MESSAGE: + main_loop = run_with_message_protocol; + break; + default: + abort(); + break; + } + protocol_type_ = _protocol_type; +} + +int ProcessPool::start_check() { if (ipc_mode == SW_IPC_SOCKET && (stream_info_ == nullptr || stream_info_->socket == 0)) { swoole_warning("must first listen to an tcp port"); return SW_ERR; } - uint32_t i; running = started = true; master_pid = getpid(); + reload_workers = new Worker[worker_num](); + swoole_set_process_type(SW_PROCESS_MASTER); - for (i = 0; i < worker_num; i++) { + if (async) { + main_loop = run_async; + } + + SW_LOOP_N(worker_num) { workers[i].pool = this; workers[i].id = start_id + i; workers[i].type = type; + if (workers[i].pipe_worker) { + workers[i].pipe_worker->buffer_size = UINT_MAX; + } + if (workers[i].pipe_master) { + workers[i].pipe_master->buffer_size = UINT_MAX; + } } - for (i = 0; i < worker_num; i++) { + return SW_OK; +} + +/** + * start workers + */ +int ProcessPool::start() { + if (start_check() < 0) { + return SW_ERR; + } + + if (onStart) { + onStart(this); + } + + SW_LOOP_N(worker_num) { if (spawn(&(workers[i])) < 0) { return SW_ERR; } @@ -208,7 +291,7 @@ int ProcessPool::schedule() { for (i = 0; i < worker_num + 1; i++) { target_worker_id = sw_atomic_fetch_add(&round_id, 1) % worker_num; - if (workers[target_worker_id].status == SW_WORKER_IDLE) { + if (workers[target_worker_id].is_idle()) { found = 1; break; } @@ -229,7 +312,7 @@ int ProcessPool::response(const char *data, int length) { } int ProcessPool::push_message(EventData *msg) { - if (message_box->push(msg, sizeof(msg->info) + msg->info.len) < 0) { + if (message_box->push(msg, msg->size()) < 0) { return SW_ERR; } return swoole_kill(master_pid, SIGIO); @@ -258,11 +341,7 @@ int ProcessPool::pop_message(void *data, size_t size) { return message_box->pop(data, size); } -/** - * dispatch data to worker - */ -int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { - int ret = 0; +swResultCode ProcessPool::dispatch(EventData *data, int *dst_worker_id) { Worker *worker; if (use_socket) { @@ -271,7 +350,7 @@ int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { return SW_ERR; } stream->response = nullptr; - if (stream->send((char *) data, sizeof(data->info) + data->info.len) < 0) { + if (stream->send((char *) data, data->size()) < 0) { stream->cancel = 1; delete stream; return SW_ERR; @@ -286,19 +365,15 @@ int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { *dst_worker_id += start_id; worker = get_worker(*dst_worker_id); - int sendn = sizeof(data->info) + data->info.len; - ret = worker->send_pipe_message(data, sendn, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); - - if (ret >= 0) { - sw_atomic_fetch_add(&worker->tasking_num, 1); - } else { - swoole_warning("send %d bytes to worker#%d failed", sendn, *dst_worker_id); + if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) < 0) { + swoole_warning("send %d bytes to worker#%d failed", data->size(), *dst_worker_id); + return SW_ERR; } - return ret; + return SW_OK; } -int ProcessPool::dispatch_blocking(const char *data, uint32_t len) { +swResultCode ProcessPool::dispatch_blocking(const char *data, uint32_t len) { assert(use_socket); network::Client _socket(stream_info_->socket->socket_type, false); @@ -319,16 +394,9 @@ int ProcessPool::dispatch_blocking(const char *data, uint32_t len) { return SW_OK; } -/** - * dispatch data to worker - * @return SW_OK/SW_ERR - */ -int ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { - int ret = 0; - int sendn = sizeof(data->info) + data->info.len; - +swResultCode ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { if (use_socket) { - return dispatch_blocking((char *) data, sendn); + return dispatch_blocking((char *) data, data->size()); } if (*dst_worker_id < 0) { @@ -338,14 +406,11 @@ int ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { *dst_worker_id += start_id; Worker *worker = get_worker(*dst_worker_id); - ret = worker->send_pipe_message(data, sendn, SW_PIPE_MASTER); - if (ret < 0) { - swoole_warning("send %d bytes to worker#%d failed", sendn, *dst_worker_id); - } else { - sw_atomic_fetch_add(&worker->tasking_num, 1); + if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER) < 0) { + swoole_warning("send %d bytes to worker#%d failed", data->size(), *dst_worker_id); + return SW_ERR; } - - return ret > 0 ? SW_OK : SW_ERR; + return SW_OK; } bool ProcessPool::reload() { @@ -358,24 +423,66 @@ bool ProcessPool::reload() { return true; } +void ProcessPool::stop(Worker *worker) { + worker->shutdown(); + + if (!swoole_event_is_available()) { + return; + } + + auto reactor = sw_reactor(); + if (worker->pipe_worker) { + swoole_event_del(worker->pipe_worker); + } + + if (onWorkerExit) { + reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, [this, worker](Reactor *reactor) { + onWorkerExit(this, worker); + if (reactor->if_exit()) { + reactor->running = false; + } + }); + } +} + +void ProcessPool::kill_all_workers(int signo) { + SW_LOOP_N(worker_num) { + swoole_kill(workers[i].pid, signo); + } +} + void ProcessPool::shutdown() { uint32_t i; int status; Worker *worker; running = 0; + if (onShutdown) { + onShutdown(this); + } + // concurrent kill for (i = 0; i < worker_num; i++) { worker = &workers[i]; if (swoole_kill(worker->pid, SIGTERM) < 0) { - swoole_sys_warning("swKill(%d) failed", worker->pid); + swoole_sys_warning("kill(%d, SIGTERM) failed", worker->pid); continue; } } + if (max_wait_time) { + swoole_timer_add((long) max_wait_time * 1000, false, [this](Timer *, TimerNode *) { kill_all_workers(); }); + } for (i = 0; i < worker_num; i++) { worker = &workers[i]; - if (swoole_waitpid(worker->pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", worker->pid); + SW_LOOP { + if (waitpid(worker->pid, &status, 0) < 0) { + if (errno == EINTR) { + sw_timer()->select(); + continue; + } + swoole_sys_warning("waitpid(%d) failed", worker->pid); + } + break; } } started = false; @@ -388,23 +495,25 @@ pid_t ProcessPool::spawn(Worker *worker) { switch (pid) { // child case 0: - /** - * Process start - */ + worker->init(); + worker->pid = SwooleG.pid; + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); + SwooleWG.worker = worker; + if (async) { + if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { + exit(254); + } + sw_reactor()->ptr = this; + } if (onWorkerStart != nullptr) { - onWorkerStart(this, worker->id); + onWorkerStart(this, worker); } - /** - * Process main loop - */ if (main_loop) { ret_code = main_loop(this, worker); } - /** - * Process stop - */ if (onWorkerStop != nullptr) { - onWorkerStop(this, worker->id); + onWorkerStop(this, worker); } exit(ret_code); break; @@ -425,36 +534,22 @@ pid_t ProcessPool::spawn(Worker *worker) { return pid; } -int ProcessPool::get_max_request() { - int task_n; - if (max_request < 1) { - return -1; - } else { - task_n = max_request; - if (max_request_grace > 0) { - task_n += swoole_system_random(1, max_request_grace); - } - } - return task_n; -} - void ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_grace) { max_request = _max_request; max_request_grace = _max_request_grace; } -static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker) { +bool ProcessPool::is_worker_running(Worker *worker) { + return running && !worker->is_shutdown() && !worker->has_exceeded_max_request(); +} + +int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { struct { long mtype; EventData buf; } out{}; - ssize_t n = 0, ret, worker_task_always = 0; - int task_n = pool->get_max_request(); - if (task_n <= 0) { - worker_task_always = 1; - task_n = 1; - } + ssize_t n = 0; /** * Use from_fd save the task_worker->id @@ -467,65 +562,62 @@ static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker) { out.mtype = worker->id + 1; } - while (pool->running && !SwooleWG.shutdown && task_n > 0) { + int read_timeout_ms = -1; + if (pool->ipc_mode == SW_IPC_UNIXSOCK) { + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + } + + while (pool->is_worker_running(worker)) { /** * fetch task */ if (pool->use_msgqueue) { n = pool->queue->pop((QueueNode *) &out, sizeof(out.buf)); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] msgrcv() failed", worker->id); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] msgrcv(%d) failed", worker->id, pool->queue->get_id()); break; } } else if (pool->use_socket) { Socket *conn = pool->stream_info_->socket->accept(); if (conn == nullptr) { - if (errno == EAGAIN || errno == EINTR) { - continue; - } else { - swoole_sys_warning("accept(%d) failed", pool->stream_info_->socket->get_fd()); + if (catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning( + "[Worker#%d] accept(%d) failed", worker->id, pool->stream_info_->socket->get_fd()); break; + } else { + goto _end; } } n = Stream::recv_blocking(conn, (void *) &out.buf, sizeof(out.buf)); - if (n < 0) { + if (n <= 0) { conn->free(); - continue; + goto _end; } pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read(&out.buf, sizeof(out.buf)); - if (n < 0 && errno != EINTR) { + n = worker->pipe_worker->read_sync(&out.buf, sizeof(out.buf), read_timeout_ms); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + break; } } - /** - * timer - */ if (n < 0) { - if (errno == EINTR && SwooleG.signal_alarm && SwooleTG.timer) { - _alarm_handler: - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); - } - continue; + goto _end; } - - if (n != (ssize_t)(out.buf.info.len + sizeof(out.buf.info))) { - swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", + if (n != (ssize_t) out.buf.size()) { + swoole_warning("[Worker#%d] bad task packet, The received data-length[%ld] is inconsistent with the " + "packet-length[%ld]", + worker->id, n, out.buf.info.len + sizeof(out.buf.info)); - continue; } - - /** - * do task - */ - worker->status = SW_WORKER_BUSY; - ret = pool->onTask(pool, &out.buf); - worker->status = SW_WORKER_IDLE; - + if (pool->onTask(pool, worker, &out.buf) < 0) { + swoole_warning("[Worker#%d] the execution of task#%ld has failed", worker->id, pool->get_task_id(&out.buf)); + } if (pool->use_socket && pool->stream_info_->last_connection) { int _end = 0; pool->stream_info_->last_connection->send_blocking((void *) &_end, sizeof(_end)); @@ -533,67 +625,119 @@ static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker) { pool->stream_info_->last_connection = nullptr; } - /** - * timer - */ - if (SwooleG.signal_alarm) { - goto _alarm_handler; + _end: + if (sw_timer()) { + sw_timer()->select(); } + } - if (ret >= 0 && !worker_task_always) { - task_n--; - } + SwooleTG.timer_scheduler = nullptr; + + return SW_OK; +} + +static int ProcessPool_recv_packet(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + ssize_t n = event->socket->read(pool->packet_buffer, pool->max_packet_size_); + if (n < 0 && errno != EINTR) { + swoole_sys_warning("failed to read(%d) pipe", event->fd); } + RecvData msg{}; + msg.info.reactor_id = -1; + msg.info.len = n; + msg.data = pool->packet_buffer; + pool->onMessage(pool, &msg); return SW_OK; } -void ProcessPool::set_protocol(int task_protocol, uint32_t max_packet_size) { - if (task_protocol) { - main_loop = ProcessPool_worker_loop; - } else { - packet_buffer = new char[max_packet_size]; - if (stream_info_) { - stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); +static int ProcessPool_recv_message(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + if (pool->message_bus->read(event->socket) <= 0) { + return SW_OK; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return SW_OK; +} + +int ProcessPool::run_async(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode == SW_IPC_UNIXSOCK && pool->onMessage) { + swoole_event_add(worker->pipe_worker, SW_EVENT_READ); + if (pool->message_bus) { + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_message); + } else { + pool->packet_buffer = new char[pool->max_packet_size_]; + if (pool->stream_info_) { + pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); + } + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_packet); } - max_packet_size_ = max_packet_size; - main_loop = ProcessPool_worker_loop_ex; } + return swoole_event_wait(); } -static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { +int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { ssize_t n; - char *data; + RecvData msg{}; + msg.info.reactor_id = -1; + + pool->packet_buffer = new char[pool->max_packet_size_]; + if (pool->stream_info_) { + pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); + } QueueNode *outbuf = (QueueNode *) pool->packet_buffer; outbuf->mtype = 0; - while (pool->running) { + int read_timeout_ms = -1; + if (pool->ipc_mode == SW_IPC_UNIXSOCK) { + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + } + + while (pool->is_worker_running(worker)) { /** * fetch task */ if (pool->use_msgqueue) { n = pool->queue->pop(outbuf, SW_MSGMAX); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] msgrcv() failed", worker->id); + /** + * A fatal error has occurred; the message queue is no longer available, and the loop must be exited. + */ + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] msgrcv(%d) failed", worker->id, pool->queue->get_id()); break; } - data = outbuf->mdata; + msg.data = outbuf->mdata; outbuf->mtype = 0; } else if (pool->use_socket) { Socket *conn = pool->stream_info_->socket->accept(); if (conn == nullptr) { - if (errno == EAGAIN || errno == EINTR) { - continue; - } else { - swoole_sys_warning("accept(%d) failed", pool->stream_info_->socket->get_fd()); + if (catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning( + "[Worker#%d] accept(%d) failed", worker->id, pool->stream_info_->socket->get_fd()); break; + } else { + goto _end; } } - int tmp = 0; - if (conn->recv_blocking(&tmp, sizeof(tmp), MSG_WAITALL) <= 0) { + uint32_t packet_len = 0; + if (conn->recv_blocking(&packet_len, sizeof(packet_len), MSG_WAITALL) <= 0) { goto _close; } - n = ntohl(tmp); + n = ntohl(packet_len); + /** + * Errors occurring during client connections do not affect subsequent requests, + * they continue after closure. + */ if (n <= 0) { goto _close; } else if (n > pool->max_packet_size_) { @@ -602,31 +746,25 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { if (conn->recv_blocking(pool->packet_buffer, n, MSG_WAITALL) <= 0) { _close: conn->free(); - continue; + goto _end; } - data = pool->packet_buffer; + msg.data = pool->packet_buffer; pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read(pool->packet_buffer, pool->max_packet_size_); - if (n < 0 && errno != EINTR) { + n = worker->pipe_worker->read_sync(pool->packet_buffer, pool->max_packet_size_, read_timeout_ms); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + break; } - data = pool->packet_buffer; + msg.data = pool->packet_buffer; } - /** - * timer - */ if (n < 0) { - if (errno == EINTR && SwooleG.signal_alarm && SwooleTG.timer) { - _alarm_handler: - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); - } - continue; + goto _end; } - pool->onMessage(pool, data, n); + msg.info.len = n; + pool->onMessage(pool, &msg); if (pool->use_socket && pool->stream_info_->last_connection) { String *resp_buf = pool->stream_info_->response_buffer; @@ -640,24 +778,79 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { pool->stream_info_->last_connection = nullptr; } - /** - * timer - */ - if (SwooleG.signal_alarm) { - goto _alarm_handler; + _end: + if (sw_timer()) { + sw_timer()->select(); } } + + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } -/** - * add a worker to pool - */ -int ProcessPool_add_worker(ProcessPool *pool, Worker *worker) { - pool->map_->emplace(std::make_pair(worker->pid, worker)); +int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + + int read_timeout_ms = -1; + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + + auto fn = [&]() -> int { + if (worker->pipe_worker->wait_event(read_timeout_ms, SW_EVENT_READ) < 0) { + return errno == EINTR ? 0 : -1; + } + if (pool->message_bus->read(worker->pipe_worker) < 0) { + return errno == EINTR ? 0 : -1; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return 1; + }; + + if (pool->message_bus == nullptr) { + pool->create_message_bus(); + } + + worker->pipe_worker->dont_restart = 1; + + while (pool->is_worker_running(worker)) { + switch (fn()) { + case 0: + if (sw_timer()) { + sw_timer()->select(); + } + break; + case 1: + break; + case -1: + default: + swoole_sys_warning("[Worker #%d]failed to read data from pipe", worker->id); + return SW_OK; + } + } + + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } +void ProcessPool::add_worker(Worker *worker) { + map_->emplace(std::make_pair(worker->pid, worker)); +} + bool ProcessPool::detach() { if (!running || !message_box) { return false; @@ -665,7 +858,7 @@ bool ProcessPool::detach() { WorkerStopMessage msg; msg.pid = getpid(); - msg.worker_id = SwooleG.process_id; + msg.worker_id = swoole_get_process_id(); if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { return false; @@ -677,20 +870,12 @@ bool ProcessPool::detach() { int ProcessPool::wait() { pid_t new_pid, reload_worker_pid = 0; - int ret; - - reload_workers = new Worker[worker_num](); - ON_SCOPE_EXIT { - delete[] reload_workers; - reload_workers = nullptr; - }; while (running) { ExitStatus exit_status = wait_process(); - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); + if (sw_timer()) { + sw_timer()->select(); } if (read_message) { EventData msg; @@ -723,12 +908,11 @@ int ProcessPool::wait() { } if (!reloading) { if (errno > 0 && errno != EINTR) { - swoole_sys_warning("[Manager] wait failed"); + swoole_sys_warning("wait() failed"); } continue; } else { if (!reload_init) { - swoole_info("reload workers"); reload_init = true; memcpy(reload_workers, workers, sizeof(Worker) * worker_num); if (max_wait_time) { @@ -745,18 +929,16 @@ int ProcessPool::wait() { if (onWorkerNotFound) { onWorkerNotFound(this, exit_status); } else { - swoole_warning("[Manager]unknown worker[pid=%d]", exit_status.get_pid()); + swoole_warning("unknown worker[pid=%d]", exit_status.get_pid()); } continue; } if (!exit_status.is_normal_exit()) { - swoole_warning("worker#%d abnormal exit, status=%d, signal=%d" - "%s", - exit_worker->id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); + exit_worker->report_error(exit_status); + if (onWorkerError) { + onWorkerError(this, exit_worker, exit_status); + } } new_pid = spawn(exit_worker); if (new_pid < 0) { @@ -778,13 +960,12 @@ int ProcessPool::wait() { continue; } reload_worker_pid = reload_workers[reload_worker_i].pid; - ret = swoole_kill(reload_worker_pid, SIGTERM); - if (ret < 0) { + if (swoole_kill(reload_worker_pid, SIGTERM) < 0) { if (errno == ECHILD) { reload_worker_i++; goto _kill_worker; } - swoole_sys_warning("[Manager]swKill(%d) failed", reload_workers[reload_worker_i].pid); + swoole_sys_warning("kill(%d) failed", reload_workers[reload_worker_i].pid); continue; } } @@ -816,21 +997,106 @@ void ProcessPool::destroy() { delete stream_info_->response_buffer; } delete stream_info_; + stream_info_ = nullptr; } if (packet_buffer) { delete[] packet_buffer; + packet_buffer = nullptr; } if (map_) { delete map_; + map_ = nullptr; } if (message_box) { message_box->destroy(); + message_box = nullptr; + } + + if (message_bus) { + delete message_bus; + message_bus = nullptr; + } + + if (reload_workers) { + delete[] reload_workers; + reload_workers = nullptr; } sw_mem_pool()->free(workers); } +void Worker::init() { + start_time = ::time(nullptr); + request_count = 0; + set_status_to_idle(); + SwooleWG.running = true; + SwooleWG.shutdown = false; +} + +void Worker::set_max_request(uint32_t max_request, uint32_t max_request_grace) { + if (max_request > 0 && max_request_grace > 0) { + max_request += swoole_system_random(1, max_request_grace); + } + SwooleWG.max_request = max_request; +} + +bool Worker::has_exceeded_max_request() { + return SwooleWG.max_request > 0 && request_count >= SwooleWG.max_request; +} + +void Worker::shutdown() { + status = SW_WORKER_EXIT; + SwooleWG.shutdown = true; +} + +bool Worker::is_shutdown() { + return SwooleWG.shutdown; +} + +bool Worker::is_running() { + return SwooleWG.running; +} + +ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { + Socket *pipe_sock; + + if (flags & SW_PIPE_MASTER) { + pipe_sock = pipe_master; + } else { + pipe_sock = pipe_worker; + } + + // message-queue + if (pool->use_msgqueue) { + struct { + long mtype; + EventData buf; + } msg; + + msg.mtype = id + 1; + memcpy(&msg.buf, buf, n); + + return pool->queue->push((QueueNode *) &msg, n) ? n : -1; + } + + if ((flags & SW_PIPE_NONBLOCK) && swoole_event_is_available()) { + return swoole_event_write(pipe_sock, buf, n); + } else { + return pipe_sock->send_blocking(buf, n); + } +} + +void Worker::report_error(const ExitStatus &exit_status) { + swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" + "%s", + exit_status.get_pid(), + id, + exit_status.get_code(), + exit_status.get_signal(), + exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); +} + } // namespace swoole diff --git a/src/os/sendfile.cc b/src/os/sendfile.cc index e8f192e7481..aa16756ad2f 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -19,7 +19,7 @@ #include #include -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { ssize_t ret; #ifdef __MACH__ @@ -41,7 +41,7 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { // sent_bytes = (off_t)size; swoole_trace( - "send file, ret:%d, out_fd:%d, in_fd:%d, offset:%jd, size:%zu", ret, out_fd, in_fd, (intmax_t) *offset, size); + "send file, ret: %zd, out_fd:%d, in_fd:%d, offset:%jd, size:%zu", ret, out_fd, in_fd, (intmax_t) *offset, size); #ifdef __MACH__ *offset += size; @@ -64,7 +64,7 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { return SW_OK; } #elif !defined(HAVE_SENDFILE) -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { char buf[SW_BUFFER_SIZE_BIG]; size_t readn = size > sizeof(buf) ? sizeof(buf) : size; ssize_t n = pread(in_fd, buf, readn, *offset); diff --git a/src/os/signal.cc b/src/os/signal.cc index d15be9aa8b8..cab4a16e1fc 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -31,6 +31,9 @@ #include #endif #endif +#ifdef __NetBSD__ +#include +#endif using swoole::Event; using swoole::Reactor; @@ -41,8 +44,9 @@ using swoole::network::Socket; #ifdef HAVE_SIGNALFD static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler); static bool swoole_signalfd_create(); -static void swoole_signalfd_clear(); +static bool swoole_signalfd_setup(Reactor *reactor); static int swoole_signalfd_event_callback(Reactor *reactor, Event *event); +static void swoole_signalfd_clear(); #endif #ifdef HAVE_KQUEUE @@ -53,9 +57,12 @@ static void swoole_signal_async_handler(int signo); #ifdef HAVE_SIGNALFD static sigset_t signalfd_mask; -static int signal_fd = 0; +static int signal_fd = -1; static pid_t signalfd_create_pid; static Socket *signal_socket = nullptr; +static inline bool swoole_signalfd_is_available() { + return signal_fd != -1; +} #endif static Signal signals[SW_SIGNO_MAX]; static int _lock = 0; @@ -95,6 +102,11 @@ SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int func = SIG_DFL; } + if (func == SIG_IGN || func == SIG_DFL) { + signals[signo].handler = nullptr; + signals[signo].activated = false; + } + struct sigaction act { }, oact{}; act.sa_handler = func; @@ -110,12 +122,16 @@ SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int return oact.sa_handler; } +SW_API bool swoole_signal_isset(int signo) { + return signals[signo].handler && signals[signo].activated; +} + /** * set new signal handler and return origin signal handler */ SignalHandler swoole_signal_set(int signo, SignalHandler handler) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { + if (SwooleG.enable_signalfd && swoole_event_is_available()) { return swoole_signalfd_set(signo, handler); } else #endif @@ -142,7 +158,7 @@ static void swoole_signal_async_handler(int signo) { sw_reactor()->singal_no = signo; } else { // discard signal - if (_lock) { + if (_lock || !SwooleG.init) { return; } _lock = 1; @@ -176,13 +192,12 @@ SignalHandler swoole_signal_get_handler(int signo) { void swoole_signal_clear(void) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { + if (SwooleG.enable_signalfd && swoole_signalfd_is_available()) { swoole_signalfd_clear(); } else #endif { - int i; - for (i = 0; i < SW_SIGNO_MAX; i++) { + SW_LOOP_N(SW_SIGNO_MAX) { if (signals[i].activated) { #ifdef HAVE_KQUEUE if (signals[i].signo != SIGCHLD && sw_reactor()) { @@ -221,28 +236,30 @@ static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler) { signals[signo].activated = true; } - if (sw_reactor()) { - if (signal_fd == 0) { - swoole_signalfd_create(); - } else { - sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); - signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); - } - swoole_signalfd_setup(sw_reactor()); + if (!swoole_event_is_available()) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_OPERATION_NOT_SUPPORT, + "The signalfd must only be used after event loop is initialized"); + return nullptr; + } + + if (swoole_signalfd_is_available()) { + sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); + signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); } + swoole_signalfd_setup(sw_reactor()); return origin_handler; } static bool swoole_signalfd_create() { - if (signal_fd != 0) { + if (swoole_signalfd_is_available()) { return false; } signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); if (signal_fd < 0) { swoole_sys_warning("signalfd() failed"); - signal_fd = 0; return false; } signal_socket = swoole::make_socket(signal_fd, SW_FD_SIGNAL); @@ -252,7 +269,7 @@ static bool swoole_signalfd_create() { signal_socket->free(); close(signal_fd); signal_socket = nullptr; - signal_fd = 0; + signal_fd = -1; return false; } signalfd_create_pid = getpid(); @@ -262,7 +279,7 @@ static bool swoole_signalfd_create() { } bool swoole_signalfd_setup(Reactor *reactor) { - if (signal_fd == 0 && !swoole_signalfd_create()) { + if (!swoole_signalfd_is_available() && !swoole_signalfd_create()) { return false; } if (!swoole_event_isset_handler(SW_FD_SIGNAL)) { @@ -285,7 +302,7 @@ bool swoole_signalfd_setup(Reactor *reactor) { } static void swoole_signalfd_clear() { - if (signal_fd) { + if (swoole_signalfd_is_available()) { if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, nullptr) < 0) { swoole_sys_warning("sigprocmask(SIG_UNBLOCK) failed"); } @@ -293,9 +310,11 @@ static void swoole_signalfd_clear() { signal_socket->free(); signal_socket = nullptr; } + sw_memset_zero(&signals, sizeof(signals)); sw_memset_zero(&signalfd_mask, sizeof(signalfd_mask)); } - SwooleG.signal_fd = signal_fd = 0; + SwooleG.signal_fd = 0; + signal_fd = -1; } static int swoole_signalfd_event_callback(Reactor *reactor, Event *event) { @@ -349,7 +368,7 @@ static SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler) signals[signo].handler = handler; signals[signo].signo = signo; signals[signo].activated = true; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sigptr = &signals[signo]; #else auto sigptr = reinterpret_cast(&signals[signo]); diff --git a/src/os/timer.cc b/src/os/timer.cc index f28e75a1b4c..a4bca6641ad 100644 --- a/src/os/timer.cc +++ b/src/os/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -23,7 +23,7 @@ namespace swoole { static int SystemTimer_set(Timer *timer, long next_msec); -bool Timer::init_system_timer() { +bool Timer::init_with_system_timer() { set = SystemTimer_set; close = [](Timer *timer) { SystemTimer_set(timer, -1); }; swoole_signal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; }); diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc index c9f066a09ca..c1db228dedd 100644 --- a/src/os/unix_socket.cc +++ b/src/os/unix_socket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,8 +18,7 @@ #include "swoole_socket.h" namespace swoole { -UnixSocket::UnixSocket(bool blocking, int _protocol) : - SocketPair(blocking), protocol_(_protocol) { +UnixSocket::UnixSocket(bool blocking, int _protocol) : SocketPair(blocking), protocol_(_protocol) { if (socketpair(AF_UNIX, protocol_, 0, socks) < 0) { swoole_sys_warning("socketpair() failed"); return; @@ -39,4 +38,4 @@ bool UnixSocket::set_buffer_size(size_t _size) { } return true; } -} +} // namespace swoole diff --git a/src/os/wait.cc b/src/os/wait.cc index 32a3cec1337..ca02eee1388 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" @@ -32,9 +32,13 @@ struct WaitTask { int status; }; +/** + * Wait, waitpid, and signal cannot be used in a multi-threaded environment; + * they are only applicable to the main thread. There is no need to treat them as thread-local variables. + */ static std::list wait_list; -static std::unordered_map waitpid_map; -static std::unordered_map child_processes; +static std::unordered_map waitpid_map; +static std::unordered_map child_processes; bool signal_ready = false; @@ -70,11 +74,6 @@ static void signal_init() { if (!signal_ready) { Reactor *reactor = SwooleTG.reactor; swoole_signal_set(SIGCHLD, signal_handler); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && !reactor->isset_handler(SW_FD_SIGNAL)) { - swoole_signalfd_setup(reactor); - } -#endif reactor->set_exit_condition(Reactor::EXIT_CONDITION_WAIT_PID, [](Reactor *reactor, size_t &event_num) -> bool { return swoole_coroutine_wait_count() == 0; @@ -93,6 +92,20 @@ pid_t System::wait(int *__stat_loc, double timeout) { return System::waitpid(-1, __stat_loc, 0, timeout); } +pid_t System::waitpid_safe(pid_t __pid, int *__stat_loc, int __options) { + if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (__options & WNOHANG))) { + return ::waitpid(__pid, __stat_loc, __options); + } + + pid_t retval; + auto success = wait_for([__pid, &retval, __stat_loc]() -> bool { + retval = ::waitpid(__pid, __stat_loc, WNOHANG); + return retval != 0; + }); + + return success ? retval : -1; +} + /** * @error: errno & swoole_get_last_error() */ @@ -123,7 +136,7 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou WaitTask task; signal_init(); task.pid = ::waitpid(__pid, __stat_loc, __options | WNOHANG); - if (task.pid > 0) { + if (task.pid != 0) { return task.pid; } @@ -142,7 +155,7 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou TimerNode *timer = nullptr; if (timeout > 0) { timer = swoole_timer_add( - timeout * 1000, + timeout, false, [](Timer *timer, TimerNode *tnode) { Coroutine *co = (Coroutine *) tnode->data; diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 58356e28b27..d948b97647f 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -25,7 +25,7 @@ namespace swoole { /** * return the package total length */ -ssize_t Protocol::default_length_func(Protocol *protocol, network::Socket *socket, const char *data, uint32_t size) { +ssize_t Protocol::default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl) { uint16_t length_offset = protocol->package_length_offset; uint8_t package_length_size = protocol->get_package_length_size ? protocol->get_package_length_size(socket) : protocol->package_length_size; @@ -38,37 +38,39 @@ ssize_t Protocol::default_length_func(Protocol *protocol, network::Socket *socke /** * no have length field, wait more data */ - if (size < length_offset + package_length_size) { - protocol->real_header_length = length_offset + package_length_size; + if (pl->buf_size < length_offset + package_length_size) { + pl->header_len = length_offset + package_length_size; return 0; } - body_length = swoole_unpack(protocol->package_length_type, data + length_offset); + body_length = swoole_unpack(protocol->package_length_type, pl->buf + length_offset); // Length error // Protocol length is not legitimate, out of bounds or exceed the allocated length if (body_length < 0) { swoole_warning("invalid package (size=%d) from socket#%u<%s:%d>", - size, - socket->fd, - socket->info.get_ip(), - socket->info.get_port()); + pl->buf_size, + socket->fd, + socket->info.get_ip(), + socket->info.get_port()); return SW_ERR; } swoole_debug("length=%d", protocol->package_body_offset + body_length); + pl->header_len = protocol->package_length_size; // total package length return protocol->package_body_offset + body_length; } int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { + RecvData rdata{}; + if (buffer->length < package_eof_len) { return SW_CONTINUE; } - int retval; - ssize_t n = buffer->split(package_eof, package_eof_len, [&](const char *data, size_t length) -> int { - if (onPackage(this, socket, data, length) < 0) { - retval = SW_CLOSE; + rdata.info.len = length; + rdata.data = data; + if (onPackage(this, socket, &rdata) < 0) { return false; } if (socket->removed) { @@ -77,12 +79,8 @@ int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { return true; }); - if (socket->removed) { + if (socket->removed || n < 0) { return SW_CLOSE; - } - - if (n < 0) { - return retval; } else if (n == 0) { return SW_CONTINUE; } else if (n < (ssize_t) buffer->length) { @@ -108,9 +106,11 @@ int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { * @return SW_OK: continue */ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) { + RecvData rdata{}; + PacketLength pl{}; ssize_t package_length; uint8_t _package_length_size = get_package_length_size ? get_package_length_size(socket) : package_length_size; - uint32_t recv_size; + uint32_t recv_size = 0; ssize_t recv_n = 0; // protocol error @@ -135,7 +135,7 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) recv_n = socket->recv(buffer->str + buffer->length, recv_size, 0); if (recv_n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv(%d, %d) failed", socket->fd, recv_size); return SW_OK; @@ -152,7 +152,9 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) if (socket->recv_wait) { if (buffer->length >= (size_t) buffer->offset) { _do_dispatch: - if (onPackage(this, socket, buffer->str, buffer->offset) < 0) { + rdata.info.len = buffer->offset; + rdata.data = buffer->str; + if (onPackage(this, socket, &rdata) < 0) { return SW_ERR; } if (socket->removed) { @@ -175,17 +177,27 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) return SW_OK; } else { _do_get_length: - package_length = get_package_length(this, socket, buffer->str, buffer->length); + pl.buf = buffer->str; + pl.buf_size = buffer->length; + // TODO: support dynamic calculation of header buffer length (recv_size) + pl.header_len = 0; + package_length = get_package_length(this, socket, &pl); // invalid package, close connection. if (package_length < 0) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_MALFORMED_DATA, + "received %zu bytes of malformed data from the client[%s:%d]", + buffer->length, + socket->info.get_ip(), + socket->info.get_port()); return SW_ERR; } // no length else if (package_length == 0) { - if (buffer->length == package_length_offset + package_length_size) { + if (buffer->length == recv_size) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, - "bad request, no length found in %ld bytes", + "bad request, no length found in %zu bytes", buffer->length); return SW_ERR; } else { @@ -227,7 +239,8 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) */ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { bool recv_again = false; - int buf_size; + size_t buf_size; + RecvData rdata{}; _recv_data: buf_size = buffer->size - buffer->length; @@ -237,9 +250,9 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { buf_size = SW_BUFFER_SIZE_STD; } - int n = socket->recv(buf_ptr, buf_size, 0); + ssize_t n = socket->recv(buf_ptr, buf_size, 0); if (n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from socket#%d failed", socket->fd); return SW_OK; @@ -268,7 +281,9 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { } } else if (memcmp(buffer->str + buffer->length - package_eof_len, package_eof, package_eof_len) == 0) { buffer->offset = buffer->length; - if (onPackage(this, socket, buffer->str, buffer->length) < 0) { + rdata.info.len = buffer->length; + rdata.data = buffer->str; + if (onPackage(this, socket, &rdata) < 0) { return SW_ERR; } if (socket->removed) { @@ -293,7 +308,7 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { if (buffer->length == buffer->size) { recv_again = true; if (buffer->size < package_max_length) { - uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); + uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize()); if (extend_size > package_max_length) { extend_size = package_max_length; } diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index 8632018a490..e9442529ece 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -1,3 +1,19 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + #include "swoole_server.h" #ifdef SW_SUPPORT_DTLS @@ -41,7 +57,7 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) { long retval = 0; Session *session = (Session *) BIO_get_data(b); - swoole_trace_log(SW_TRACE_SSL, "BIO_ctrl(BIO[0x%016lX], cmd[%d], lval[%ld], ptrval[0x%016lX])", b, cmd, lval, ptrval); + swoole_trace_log(SW_TRACE_SSL, "BIO_ctrl(BIO[%p], cmd[%d], lval[%ld], ptrval[%p])", b, cmd, lval, ptrval); switch (cmd) { case BIO_CTRL_EOF: @@ -87,6 +103,12 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) { case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: retval = 0; break; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + case BIO_CTRL_GET_KTLS_SEND: + case BIO_CTRL_GET_KTLS_RECV: + retval = 0; + break; +#endif default: swoole_warning("unknown cmd: %d", cmd); retval = 0; @@ -101,7 +123,7 @@ int BIO_create(BIO *b) { } int BIO_destroy(BIO *b) { - swoole_trace_log(SW_TRACE_SSL, "BIO_destroy(BIO[0x%016lX])\n", b); + swoole_trace_log(SW_TRACE_SSL, "BIO_destroy(BIO[%p])\n", b); return 1; } @@ -121,7 +143,16 @@ BIO_METHOD *BIO_get_methods(void) { BIO_meth_set_ctrl(_bio_methods, BIO_ctrl); BIO_meth_set_create(_bio_methods, BIO_create); BIO_meth_set_destroy(_bio_methods, BIO_destroy); + +#ifdef OPENSSL_IS_BORINGSSL + BIO_meth_set_ctrl(_bio_methods, (long (*)(BIO *, int, long, void *)) BIO_ctrl); +#else +#if OPENSSL_VERSION_NUMBER > 0x1010007fL BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, BIO_info_cb *)) BIO_callback_ctrl); +#else + BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, bio_info_cb *)) BIO_callback_ctrl); +#endif +#endif return _bio_methods; } @@ -159,6 +190,10 @@ bool Session::init() { } bool Session::listen() { +#ifdef OPENSSL_IS_BORINGSSL + return SSL_is_dtls(socket->ssl); +#else + if (listened) { return false; } @@ -171,16 +206,16 @@ bool Session::listen() { } else if (retval < 0) { int reason = ERR_GET_REASON(ERR_peek_error()); swoole_warning("DTLSv1_listen() failed, client[%s:%d], reason=%d, error_string=%s", - socket->info.get_ip(), - socket->info.get_port(), - reason, - swoole_ssl_get_error()); + socket->info.get_ip(), + socket->info.get_port(), + reason, + swoole_ssl_get_error()); return false; } else { listened = true; } - return true; +#endif } //------------------------------------------------------------------------------- diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 44a7838d99a..aa5d8c68335 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -10,20 +10,25 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_http.h" #include "swoole_server.h" +#include "swoole_proxy.h" +#include "swoole_base64.h" #include +#include #include "swoole_util.h" #include "swoole_http2.h" #include "swoole_websocket.h" #include "swoole_static_handler.h" +#include "thirdparty/multipart_parser.h" + using std::string; using swoole::http_server::Request; using swoole::http_server::StaticHandler; @@ -40,6 +45,101 @@ static const char *method_strings[] = { namespace swoole { +std::string HttpProxy::get_auth_str() { + char auth_buf[256]; + char encode_buf[512]; + size_t n = sw_snprintf(auth_buf, + sizeof(auth_buf), + "%.*s:%.*s", + (int) username.length(), + username.c_str(), + (int) password.length(), + password.c_str()); + base64_encode((unsigned char *) auth_buf, n, encode_buf); + return std::string(encode_buf); +} + +size_t HttpProxy::pack(String *send_buffer, const std::string *host_name) { + if (!password.empty()) { + auto auth_str = get_auth_str(); + return sw_snprintf(send_buffer->str, + send_buffer->size, + SW_HTTP_PROXY_FMT "Proxy-Authorization: Basic %.*s\r\n\r\n", + (int) target_host.length(), + target_host.c_str(), + target_port, + (int) host_name->length(), + host_name->c_str(), + target_port, + (int) auth_str.length(), + auth_str.c_str()); + } else { + return sw_snprintf(send_buffer->str, + send_buffer->size, + SW_HTTP_PROXY_FMT "\r\n", + (int) target_host.length(), + target_host.c_str(), + target_port, + (int) host_name->length(), + host_name->c_str(), + target_port); + } +} + +bool HttpProxy::handshake(String *recv_buffer) { + bool ret = false; + char *buf = recv_buffer->str; + size_t len = recv_buffer->length; + int state = 0; + char *p = buf; + char *pe = buf + len; + + if (recv_buffer->length < sizeof(SW_HTTP_PROXY_HANDSHAKE_RESPONSE) - 1) { + return false; + } + + for (; p < buf + len; p++) { + if (state == 0) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { + state = 1; + p += sizeof("HTTP/1.x") - 1; + } else { + break; + } + } else if (state == 1) { + if (isspace(*p)) { + continue; + } else { + if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { + state = 2; + p += sizeof("200") - 1; + } else { + break; + } + } + } else if (state == 2) { + ret = true; + break; + /** + * The response message is generally "Connection established," + * although it is not specified in the RFC documents, and thus will not be checked for now. + */ +#if SW_HTTP_PROXY_CHECK_MESSAGE + if (isspace(*p)) { + continue; + } else { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { + ret = true; + } + break; + } +#endif + } + } + + return ret; +} + bool Server::select_static_handler(http_server::Request *request, Connection *conn) { const char *url = request->buffer_->str + request->url_offset_; size_t url_length = request->url_length_; @@ -73,16 +173,16 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co auto date_str = handler.get_date(); auto date_str_last_modified = handler.get_date_last_modified(); - string date_if_modified_since = request->get_date_if_modified_since(); + string date_if_modified_since = request->get_header("If-Modified-Since"); if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) { response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 304 Not Modified\r\n" - "%s" + "Connection: %s\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", + request->keep_alive ? "keep-alive" : "close", date_str.c_str(), date_str_last_modified.c_str(), SW_HTTP_SERVER_SOFTWARE); @@ -92,44 +192,32 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } - auto task = handler.get_task(); - - std::set dir_files; - std::string index_file = ""; /** * if http_index_files is enabled, need to search the index file first. * if the index file is found, set filename to index filename. */ - if (http_index_files && !http_index_files->empty() && handler.is_dir()) { - handler.get_dir_files(dir_files); - index_file = swoole::intersection(*http_index_files, dir_files); - - if (index_file != "" && !handler.set_filename(index_file)) { - return false; - } else if (index_file == "" && !http_autoindex) { - return false; - } + if (!handler.hit_index_file()) { + return false; } + /** * the index file was not found in the current directory, * if http_autoindex is enabled, should show the list of files in the current directory. */ - if (index_file == "" && http_autoindex && handler.is_dir()) { - if (dir_files.empty()) { - handler.get_dir_files(dir_files); - } - size_t body_length = handler.get_index_page(dir_files, sw_tg_buffer()->str, sw_tg_buffer()->size); + if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) { + sw_tg_buffer()->clear(); + size_t body_length = handler.make_index_page(sw_tg_buffer()); response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 200 OK\r\n" - "%s" + "Connection: %s\r\n" "Content-Length: %ld\r\n" "Content-Type: text/html\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", + request->keep_alive ? "keep-alive" : "close", (long) body_length, date_str.c_str(), date_str_last_modified.c_str(), @@ -143,21 +231,38 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } - response.info.len = sw_snprintf(header_buffer, - sizeof(header_buffer), - "HTTP/1.1 200 OK\r\n" - "%s" - "Content-Length: %ld\r\n" - "Content-Type: %s\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", - (long) task->length, - handler.get_mimetype(), - date_str.c_str(), - date_str_last_modified.c_str(), - SW_HTTP_SERVER_SOFTWARE); + handler.parse_range(request->get_header("Range").c_str(), request->get_header("If-Range").c_str()); + auto tasks = handler.get_tasks(); + + std::stringstream header_stream; + if (1 == tasks.size()) { + if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { + header_stream << "Content-Range: bytes " << tasks[0].offset << "-" + << (tasks[0].length + tasks[0].offset - 1) << "/" << handler.get_filesize() << "\r\n"; + } else { + header_stream << "Accept-Ranges: bytes\r\n"; + } + } + + response.info.len = + sw_snprintf(header_buffer, + sizeof(header_buffer), + "HTTP/1.1 %s\r\n" + "Connection: %s\r\n" + "Content-Length: %ld\r\n" + "Content-Type: %s\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Server: %s\r\n\r\n", + http_server::get_status_message(handler.status_code), + request->keep_alive ? "keep-alive" : "close", + SW_HTTP_HEAD == request->method ? 0 : handler.get_content_length(), + SW_HTTP_HEAD == request->method ? handler.get_mimetype() : handler.get_content_type(), + header_stream.str().c_str(), + date_str.c_str(), + date_str_last_modified.c_str(), + SW_HTTP_SERVER_SOFTWARE); response.data = header_buffer; @@ -168,11 +273,40 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co send_to_connection(&response); // Send HTTP body - if (task->length != 0) { - response.info.type = SW_SERVER_EVENT_SEND_FILE; - response.info.len = sizeof(*task) + task->length + 1; - response.data = (char *) task; - send_to_connection(&response); + if (SW_HTTP_HEAD != request->method) { + if (!tasks.empty()) { + size_t task_size = sizeof(network::SendfileTask) + strlen(handler.get_filename()) + 1; + network::SendfileTask *task = (network::SendfileTask *) sw_malloc(task_size); + strcpy(task->filename, handler.get_filename()); + if (tasks.size() > 1) { + for (auto i = tasks.begin(); i != tasks.end(); i++) { + response.info.type = SW_SERVER_EVENT_SEND_DATA; + response.info.len = strlen(i->part_header); + response.data = i->part_header; + send_to_connection(&response); + + task->offset = i->offset; + task->length = i->length; + response.info.type = SW_SERVER_EVENT_SEND_FILE; + response.info.len = task_size; + response.data = (char *) task; + send_to_connection(&response); + } + + response.info.type = SW_SERVER_EVENT_SEND_DATA; + response.info.len = strlen(handler.get_end_part()); + response.data = handler.get_end_part(); + send_to_connection(&response); + } else if (tasks[0].length > 0) { + task->offset = tasks[0].offset; + task->length = tasks[0].length; + response.info.type = SW_SERVER_EVENT_SEND_FILE; + response.info.len = task_size; + response.data = (char *) task; + send_to_connection(&response); + } + sw_free(task); + } } // Close the connection if keepalive is not used @@ -195,9 +329,195 @@ void Server::destroy_http_request(Connection *conn) { conn->object = nullptr; } +void Server::add_http_compression_type(const std::string &type) { + if (http_compression_types == nullptr) { + http_compression_types = std::make_shared>(); + } + http_compression_types->emplace(type); +} + namespace http_server { //----------------------------------------------------------------- +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + Request *request = (Request *) p->data; + request->form_data_->current_header_name = at; + request->form_data_->current_header_name_len = length; + + swoole_trace("header_field: at=%.*s, length=%lu", (int) length, at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("header_value: at=%.*s, length=%lu", (int) length, at, length); + + Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; + + form_data->multipart_buffer_->append(form_data->current_header_name, form_data->current_header_name_len); + form_data->multipart_buffer_->append(SW_STRL(": ")); + form_data->multipart_buffer_->append(at, length); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); + + if (SW_STRCASEEQ(form_data->current_header_name, form_data->current_header_name_len, "content-disposition")) { + ParseCookieCallback cb = [request, form_data, p](char *key, size_t key_len, char *value, size_t value_len) { + if (SW_STRCASEEQ(key, key_len, "filename")) { + memcpy(form_data->upload_tmpfile->str, + form_data->upload_tmpfile_fmt_.c_str(), + form_data->upload_tmpfile_fmt_.length()); + form_data->upload_tmpfile->str[form_data->upload_tmpfile_fmt_.length()] = 0; + form_data->upload_filesize = 0; + int tmpfile = swoole_tmpfile(form_data->upload_tmpfile->str); + if (tmpfile < 0) { + request->excepted = true; + return false; + } + + FILE *fp = fdopen(tmpfile, "wb+"); + if (fp == nullptr) { + swoole_sys_warning("fopen(%s) failed", form_data->upload_tmpfile->str); + return false; + } + p->fp = fp; + + return false; + } + return true; + }; + parse_cookie(at, length, cb); + } + + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + auto request = (Request *) p->data; + auto form_data = request->form_data_; + swoole_trace("on_data: length=%lu", length); + + if (!p->fp) { + if (form_data->multipart_buffer_->length + length > request->max_length_) { + request->excepted = 1; + request->unavailable = 1; + return 1; + } + form_data->multipart_buffer_->append(at, length); + return 0; + } + + form_data->upload_filesize += length; + if (form_data->upload_filesize > form_data->upload_max_filesize) { + request->excepted = 1; + request->too_large = 1; + return 1; + } + + ssize_t n = fwrite(at, sizeof(char), length, p->fp); + if (n != (off_t) length) { + fclose(p->fp); + p->fp = nullptr; + request->excepted = 1; + request->unavailable = 1; + swoole_sys_warning("failed to write upload file"); + return 1; + } + + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete"); + Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; + if (p->fp) { + form_data->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); + form_data->multipart_buffer_->append(form_data->upload_tmpfile->str, strlen(form_data->upload_tmpfile->str)); + } + request->multipart_header_parsed = 1; + form_data->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end\n"); + Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; + request->multipart_header_parsed = 0; + if (p->fp) { + form_data->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); + fflush(p->fp); + fclose(p->fp); + p->fp = nullptr; + } + form_data->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begin"); + Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; + form_data->multipart_buffer_->append(p->boundary, p->boundary_length); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; + form_data->multipart_buffer_->append(p->boundary, p->boundary_length); + form_data->multipart_buffer_->append(SW_STRL("--")); + + request->content_length_ = form_data->multipart_buffer_->length - request->header_length_; + request->tried_to_dispatch = 1; + +#if 0 + /** + * Replace content-length with the actual value + */ + char *ptr = request->multipart_buffer_->str - (sizeof("\r\n\r\n") - 1); + char *ptr_end = request->multipart_buffer_->str + (request->multipart_buffer_->length - (sizeof("\r\n\r\n") - 1)); + + for (; ptr < ptr_end; ptr++) { + if (SW_STR_ISTARTS_WITH(ptr, ptr_end - ptr, "Content-Length:")) { + ptr += (sizeof("Content-Length:") - 1); + // skip spaces + while (*ptr == ' ') { + ptr++; + } + break; + } + } + + std::string actual_content_length = std::to_string(request->content_length_); + memcpy(ptr, actual_content_length.c_str(), actual_content_length.length()); + + ptr += actual_content_length.length(); + SW_LOOP { + if (*ptr == '\r') { + break; + } else { + *ptr = ' '; + ptr++; + } + } +#endif + + swoole_trace("end, buffer=%.*s", (int) form_data->multipart_buffer_->length, form_data->multipart_buffer_->str); + + return 0; +} + +static const multipart_parser_settings mt_parser_settings = { + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + const char *get_status_message(int code) { switch (code) { case 100: @@ -290,6 +610,8 @@ const char *get_status_message(int code) { return "429 Too Many Requests"; case 431: return "431 Request Header Fields Too Large"; + case 451: + return "451 Unavailable For Legal Reasons"; case 500: return "500 Internal Server Error"; case 501: @@ -318,6 +640,89 @@ const char *get_status_message(int code) { } } +void parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb) { + char *key, *value; + const char *separator = ";\0"; + size_t key_len = 0; + char *strtok_buf = nullptr; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + key = strtok_r(_c, separator, &strtok_buf); + while (key) { + size_t value_len; + value = strchr(key, '='); + + while (isspace(*key)) { + key++; + } + + if (key == value || *key == '\0') { + goto next_cookie; + } + + if (value) { + *value++ = '\0'; + value_len = strlen(value); + } else { + value = (char *) ""; + value_len = 0; + } + + key_len = strlen(key); + if (!cb(key, key_len, value, value_len)) { + break; + } + next_cookie: + key = strtok_r(NULL, separator, &strtok_buf); + } +} + +bool parse_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { + while (offset < length) { + if (at[offset] == ' ' || at[offset] == ';') { + offset++; + continue; + } + if (SW_STR_ISTARTS_WITH(at + offset, length - offset, "boundary=")) { + offset += sizeof("boundary=") - 1; + break; + } + void *delimiter = memchr((void *) (at + offset), ';', length - offset); + if (delimiter == nullptr) { + return false; + } else { + offset += (const char *) delimiter - (at + offset); + } + } + + int boundary_len = length - offset; + char *boundary_str = (char *) at + offset; + // find eof of boundary + if (boundary_len > 0) { + // find ';' + char *tmp = (char *) memchr(boundary_str, ';', boundary_len); + if (tmp) { + boundary_len = tmp - boundary_str; + } + } + if (boundary_len <= 0) { + return false; + } + // trim '"' + if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { + boundary_str++; + boundary_len -= 2; + } + *out_boundary_str = boundary_str; + *out_boundary_len = boundary_len; + + return true; +} + static int url_htoi(char *s) { int value; int c; @@ -451,7 +856,6 @@ int Request::get_protocol() { method = SW_HTTP_PURGE; p += 5; } -#ifdef SW_USE_HTTP2 // HTTP2 Connection Preface else if (memcmp(p, SW_STRL("PRI")) == 0) { method = SW_HTTP_PRI; @@ -461,9 +865,7 @@ int Request::get_protocol() { } else { goto _excepted; } - } -#endif - else { + } else { _excepted: excepted = 1; return SW_ERR; @@ -491,7 +893,7 @@ int Request::get_protocol() { if (isspace(*p)) { continue; } - if ((size_t)(pe - p) < (sizeof("HTTP/1.x") - 1)) { + if ((size_t) (pe - p) < (sizeof("HTTP/1.x") - 1)) { return SW_ERR; } if (memcmp(p, SW_STRL("HTTP/1.1")) == 0) { @@ -524,37 +926,45 @@ void Request::parse_header_info() { for (; p < pe; p++) { if (*(p - 1) == '\n' && *(p - 2) == '\r') { - if (SW_STRCASECT(p, pe - p, "Content-Length:")) { - unsigned long long content_length; + if (SW_STR_ISTARTS_WITH(p, pe - p, "Content-Length:")) { // strlen("Content-Length:") p += (sizeof("Content-Length:") - 1); // skip spaces while (*p == ' ') { p++; } - content_length = strtoull(p, nullptr, 10); - content_length_ = SW_MIN(content_length, UINT32_MAX); + content_length_ = strtoull(p, nullptr, 10); known_length = 1; - } else if (SW_STRCASECT(p, pe - p, "Connection:")) { + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection:")) { // strlen("Connection:") p += (sizeof("Connection:") - 1); // skip spaces while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "keep-alive")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "keep-alive")) { keep_alive = 1; } - } else if (SW_STRCASECT(p, pe - p, "Transfer-Encoding:")) { + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Transfer-Encoding:")) { // strlen("Transfer-Encoding:") p += (sizeof("Transfer-Encoding:") - 1); // skip spaces while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "chunked")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "chunked")) { chunked = 1; } + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Content-Type:")) { + p += (sizeof("Content-Type:") - 1); + while (*p == ' ') { + p++; + } + if (SW_STR_ISTARTS_WITH(p, pe - p, "multipart/form-data")) { + form_data_ = new FormData(); + form_data_->multipart_boundary_buf = p + (sizeof("multipart/form-data") - 1); + form_data_->multipart_boundary_len = strchr(p, '\r') - form_data_->multipart_boundary_buf; + } } } } @@ -565,22 +975,96 @@ void Request::parse_header_info() { } } -#ifdef SW_HTTP_100_CONTINUE +bool Request::init_multipart_parser(Server *server) { + char *boundary_str; + int boundary_len; + if (!parse_multipart_boundary( + form_data_->multipart_boundary_buf, form_data_->multipart_boundary_len, 0, &boundary_str, &boundary_len)) { + return false; + } + + form_data_->multipart_parser_ = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + if (!form_data_->multipart_parser_) { + swoole_warning("multipart_parser_init() failed"); + return false; + } + form_data_->multipart_parser_->data = this; + + auto tmp_buffer = new String(SW_BUFFER_SIZE_BIG); + tmp_buffer->append(buffer_->str + header_length_, buffer_->length - header_length_); + form_data_->multipart_buffer_ = buffer_; + form_data_->multipart_buffer_->length = header_length_; + buffer_ = tmp_buffer; + form_data_->upload_tmpfile_fmt_ = server->upload_tmp_dir + "/swoole.upfile.XXXXXX"; + form_data_->upload_tmpfile = new String(form_data_->upload_tmpfile_fmt_); + form_data_->upload_max_filesize = server->upload_max_filesize; + + return true; +} + +void Request::destroy_multipart_parser() { + auto tmp_buffer = buffer_; + delete tmp_buffer; + buffer_ = form_data_->multipart_buffer_; + form_data_->multipart_buffer_ = nullptr; + if (form_data_->multipart_parser_->fp) { + fclose(form_data_->multipart_parser_->fp); + unlink(form_data_->upload_tmpfile->str); + } + multipart_parser_free(form_data_->multipart_parser_); + form_data_->multipart_parser_ = nullptr; + delete form_data_->upload_tmpfile; + form_data_->upload_tmpfile = nullptr; + delete form_data_; + form_data_ = nullptr; +} + +bool Request::parse_multipart_data(String *buffer) { + excepted = 0; + ssize_t n = multipart_parser_execute(form_data_->multipart_parser_, buffer->str, buffer->length); + swoole_trace("multipart_parser_execute: buffer->length=%lu, n=%lu\n", buffer->length, n); + if (n < 0) { + int l_error = + multipart_parser_error_msg(form_data_->multipart_parser_, sw_tg_buffer()->str, sw_tg_buffer()->size); + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, reason: %.*s", + l_error, + sw_tg_buffer()->str); + return false; + } else if ((size_t) n != buffer->length) { + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + buffer->length); + return excepted; + } + buffer->clear(); + return true; +} + +Request::~Request() { + if (form_data_) { + destroy_multipart_parser(); + } +} + bool Request::has_expect_header() { // char *buf = buffer->str + buffer->offset; char *buf = buffer_->str; // int len = buffer->length - buffer->offset; - int len = buffer_->length; + size_t len = buffer_->length; char *pe = buf + len; char *p; for (p = buf; p < pe; p++) { - if (*p == '\r' && pe - p > sizeof("\r\nExpect")) { + if (*p == '\r' && (size_t) (pe - p) > sizeof("\r\nExpect")) { p += 2; - if (SW_STRCASECT(p, pe - p, "Expect: ")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Expect: ")) { p += sizeof("Expect: ") - 1; - if (SW_STRCASECT(p, pe - p, "100-continue")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "100-continue")) { return true; } else { return false; @@ -592,7 +1076,6 @@ bool Request::has_expect_header() { } return false; } -#endif int Request::get_header_length() { char *p = buffer_->str + buffer_->offset; @@ -615,7 +1098,7 @@ int Request::get_chunked_body_length() { char *pe = buffer_->str + buffer_->length; while (1) { - if ((size_t)(pe - p) < (1 + (sizeof("\r\n") - 1))) { + if ((size_t) (pe - p) < (1 + (sizeof("\r\n") - 1))) { /* need the next chunk */ return SW_ERR; } @@ -644,34 +1127,50 @@ int Request::get_chunked_body_length() { return SW_OK; } -string Request::get_date_if_modified_since() { +string Request::get_header(const char *name) { + size_t name_len = strlen(name); char *p = buffer_->str + url_offset_ + url_length_ + 10; char *pe = buffer_->str + header_length_; - string result; - - char *date_if_modified_since = nullptr; - size_t length_if_modified_since = 0; + char *buffer = nullptr; + char *colon = nullptr; int state = 0; + int i = 0; + + bool is_error_header_name = false; + for (; p < pe; p++) { switch (state) { case 0: - if (SW_STRCASECT(p, pe - p, "If-Modified-Since")) { - p += sizeof("If-Modified-Since"); + if (SW_STR_ISTARTS_WITH(p, pe - p, "\r\n")) { + i = 0; + is_error_header_name = false; + break; + } + + if (!is_error_header_name && swoole_str_istarts_with(p, pe - p, name, name_len)) { + colon = p + name_len; + if (colon[0] != ':' || i > 1) { + is_error_header_name = true; + break; + } + + p += name_len; state = 1; } + + i++; break; case 1: if (!isspace(*p)) { - date_if_modified_since = p; + buffer = p; state = 2; } break; case 2: - if (SW_STRCASECT(p, pe - p, "\r\n")) { - length_if_modified_since = p - date_if_modified_since; - return string(date_if_modified_since, length_if_modified_since); + if (SW_STR_ISTARTS_WITH(p, pe - p, "\r\n")) { + return string(buffer, p - buffer); } break; default: @@ -679,7 +1178,7 @@ string Request::get_date_if_modified_since() { } } - return string(""); + return string(); } int get_method(const char *method_str, size_t method_len) { @@ -699,10 +1198,15 @@ const char *get_method_string(int method) { return method_strings[method - 1]; } -//----------------------------------------------------------------- - -#ifdef SW_USE_HTTP2 +int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) { + if (serv->is_unavailable()) { + _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); + return SW_ERR; + } + return Server::dispatch_task(proto, _socket, rdata); +} +//----------------------------------------------------------------- static void protocol_status_error(Socket *socket, Connection *conn) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PROTOCOL_ERROR, @@ -712,12 +1216,12 @@ static void protocol_status_error(Socket *socket, Connection *conn) { conn->info.get_port()); } -ssize_t get_package_length(Protocol *protocol, Socket *socket, const char *data, uint32_t length) { +ssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLength *pl) { Connection *conn = (Connection *) socket->object; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { - return websocket::get_package_length(protocol, socket, data, length); + return websocket::get_package_length(protocol, socket, pl); } else if (conn->http2_stream) { - return http2::get_frame_length(protocol, socket, data, length); + return http2::get_frame_length(protocol, socket, pl); } else { protocol_status_error(socket, conn); return SW_ERR; @@ -727,7 +1231,7 @@ ssize_t get_package_length(Protocol *protocol, Socket *socket, const char *data, uint8_t get_package_length_size(Socket *socket) { Connection *conn = (Connection *) socket->object; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { - return SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); + return SW_WEBSOCKET_MESSAGE_HEADER_SIZE; } else if (conn->http2_stream) { return SW_HTTP2_FRAME_HEADER_SIZE; } else { @@ -736,17 +1240,16 @@ uint8_t get_package_length_size(Socket *socket) { } } -int dispatch_frame(Protocol *proto, Socket *socket, const char *data, uint32_t length) { +int dispatch_frame(const Protocol *proto, Socket *socket, const RecvData *rdata) { Connection *conn = (Connection *) socket->object; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { - return websocket::dispatch_frame(proto, socket, data, length); + return websocket::dispatch_frame(proto, socket, rdata); } else if (conn->http2_stream) { - return Server::dispatch_task(proto, socket, data, length); + return Server::dispatch_task(proto, socket, rdata); } else { protocol_status_error(socket, conn); return SW_ERR; } } -#endif } // namespace http_server } // namespace swoole diff --git a/src/protocol/http2.cc b/src/protocol/http2.cc index 243e03edf4e..b870eda4eb4 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -12,47 +12,119 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" #include "swoole_socket.h" #include "swoole_http2.h" +#include "swoole_protocol.h" +using swoole::PacketLength; using swoole::Protocol; using swoole::network::Socket; namespace swoole { namespace http2 { -int send_setting_frame(Protocol *protocol, Socket *_socket) { - char setting_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 3]; - char *p = setting_frame; +static Settings default_settings = { + SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE, + SW_HTTP2_DEFAULT_ENABLE_PUSH, + SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, + SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE, + SW_HTTP2_DEFAULT_MAX_FRAME_SIZE, + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE, +}; + +void put_default_setting(enum swHttp2SettingId id, uint32_t value) { + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + default_settings.header_table_size = value; + break; + case SW_HTTP2_SETTINGS_ENABLE_PUSH: + default_settings.enable_push = value; + break; + case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + default_settings.max_concurrent_streams = value; + break; + case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: + default_settings.init_window_size = value; + break; + case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: + default_settings.max_frame_size = value; + break; + case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + default_settings.max_header_list_size = value; + break; + default: + assert(0); + break; + } +} + +uint32_t get_default_setting(enum swHttp2SettingId id) { + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + return default_settings.header_table_size; + case SW_HTTP2_SETTINGS_ENABLE_PUSH: + return default_settings.enable_push; + case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return default_settings.max_concurrent_streams; + case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: + return default_settings.init_window_size; + case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: + return default_settings.max_frame_size; + case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return default_settings.max_header_list_size; + default: + assert(0); + return 0; + } +} + +static inline void pack_setting_item(char *_buf, enum swHttp2SettingId _id, uint32_t _value) { uint16_t id; uint32_t value; + id = htons(_id); + memcpy(_buf, &id, sizeof(id)); + value = htonl(_value); + memcpy(_buf + 2, &value, sizeof(value)); +} - set_frame_header(p, SW_HTTP2_TYPE_SETTINGS, SW_HTTP2_SETTING_OPTION_SIZE * 3, 0, 0); +size_t pack_setting_frame(char *buf, const Settings &settings, bool server_side) { + char *p = buf; + size_t size = SW_HTTP2_SETTING_OPTION_SIZE * (server_side ? 5 : 6); + set_frame_header(p, SW_HTTP2_TYPE_SETTINGS, size, 0, 0); p += SW_HTTP2_FRAME_HEADER_SIZE; - id = htons(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTING_HEADER_TABLE_SIZE, settings.header_table_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; + + if (!server_side) { + pack_setting_item(p, SW_HTTP2_SETTINGS_ENABLE_PUSH, settings.enable_push); + p += SW_HTTP2_SETTING_OPTION_SIZE; + } + + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, settings.max_concurrent_streams); p += SW_HTTP2_SETTING_OPTION_SIZE; - id = htons(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_DEFAULT_WINDOW_SIZE); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, settings.init_window_size); p += SW_HTTP2_SETTING_OPTION_SIZE; - id = htons(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_MAX_MAX_FRAME_SIZE); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, settings.max_frame_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; - return _socket->send(setting_frame, sizeof(setting_frame), 0); + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, settings.max_header_list_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; + + return p - buf; +} + +int send_setting_frame(Protocol *protocol, Socket *_socket) { + char setting_frame[SW_HTTP2_SETTING_FRAME_SIZE]; + size_t n = pack_setting_frame(setting_frame, default_settings, true); + return _socket->send(setting_frame, n, 0); } /** @@ -66,11 +138,11 @@ int send_setting_frame(Protocol *protocol, Socket *_socket) { | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -ssize_t get_frame_length(Protocol *protocol, Socket *conn, const char *buf, uint32_t length) { - if (length < SW_HTTP2_FRAME_HEADER_SIZE) { +ssize_t get_frame_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { + if (pl->buf_size < SW_HTTP2_FRAME_HEADER_SIZE) { return 0; } - return get_length(buf) + SW_HTTP2_FRAME_HEADER_SIZE; + return get_length(pl->buf) + SW_HTTP2_FRAME_HEADER_SIZE; } const char *get_type(int type) { @@ -96,7 +168,7 @@ const char *get_type(int type) { case SW_HTTP2_TYPE_CONTINUATION: return "CONTINUATION"; default: - return "UNKOWN"; + return "UNKNOWN"; } } diff --git a/src/server/message_bus.cc b/src/protocol/message_bus.cc similarity index 81% rename from src/server/message_bus.cc rename to src/protocol/message_bus.cc index f059625df0c..0f6676daf09 100644 --- a/src/server/message_bus.cc +++ b/src/protocol/message_bus.cc @@ -1,4 +1,4 @@ -#include "swoole_server.h" +#include "swoole_message_bus.h" #include @@ -67,12 +67,17 @@ ReturnCode MessageBus::prepare_packet(uint16_t &recv_chunk_count, String *packet */ buffer_->info.flags |= SW_EVENT_DATA_OBJ_PTR; memcpy(buffer_->data, &packet_buffer, sizeof(packet_buffer)); - swoole_trace("msg_id=%ld, len=%u", buffer_->info.msg_id, buffer_->info.len); + swoole_trace("msg_id=%" PRIu64 ", len=%u", buffer_->info.msg_id, buffer_->info.len); return SW_READY; } } +/** + * @return -1: a fatal error has occurred and needs to be terminated + * @return 0: continue + * @return >0: success + */ ssize_t MessageBus::read(Socket *sock) { ssize_t recv_n = 0; uint16_t recv_chunk_count = 0; @@ -82,7 +87,7 @@ ssize_t MessageBus::read(Socket *sock) { _read_from_pipe: recv_n = recv(sock->get_fd(), info, sizeof(buffer_->info), MSG_PEEK); if (recv_n < 0) { - if (sock->catch_error(errno) == SW_WAIT) { + if (sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } return SW_ERR; @@ -99,7 +104,7 @@ ssize_t MessageBus::read(Socket *sock) { if (packet_buffer == nullptr) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, - "abnormal pipeline data, msg_id=%ld, pipe_fd=%d, reactor_id=%d", + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", info->msg_id, sock->get_fd(), info->reactor_id); @@ -117,12 +122,12 @@ ssize_t MessageBus::read(Socket *sock) { swoole_warning("receive pipeline data error, pipe_fd=%d, reactor_id=%d", sock->get_fd(), info->reactor_id); return SW_ERR; } - if (recv_n < 0 && sock->catch_error(errno) == SW_WAIT) { + if (recv_n < 0 && sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } if (recv_n > 0) { packet_buffer->length += (recv_n - sizeof(buffer_->info)); - swoole_trace("append msgid=%ld, buffer=%p, n=%ld", buffer_->info.msg_id, packet_buffer, recv_n); + swoole_trace("append msgid=%" PRIu64 ", buffer=%p, n=%ld", buffer_->info.msg_id, packet_buffer, recv_n); } switch (prepare_packet(recv_chunk_count, packet_buffer)) { @@ -148,7 +153,7 @@ ssize_t MessageBus::read_with_buffer(network::Socket *sock) { _read_from_pipe: recv_n = sock->read(buffer_, buffer_size_); if (recv_n < 0) { - if (sock->catch_error(errno) == SW_WAIT) { + if (sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } return SW_ERR; @@ -167,7 +172,7 @@ ssize_t MessageBus::read_with_buffer(network::Socket *sock) { if (packet_buffer == nullptr) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, - "abnormal pipeline data, msg_id=%ld, pipe_fd=%d, reactor_id=%d", + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", buffer_->info.msg_id, sock->get_fd(), buffer_->info.reactor_id); @@ -223,7 +228,15 @@ bool MessageBus::write(Socket *sock, SendData *resp) { iov[0].iov_len = sizeof(resp->info); iov[1].iov_base = (void *) payload; iov[1].iov_len = l_payload; - return send_fn(sock, iov, 2) == (ssize_t)(sizeof(resp->info) + l_payload); + + if (send_fn(sock, iov, 2) == (ssize_t) (sizeof(resp->info) + l_payload)) { + return true; + } + if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_BUFFER_SIZE_STD) { + max_length = SW_IPC_BUFFER_SIZE; + } else { + return false; + } } resp->info.flags = SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN; @@ -245,15 +258,13 @@ bool MessageBus::write(Socket *sock, SendData *resp) { swoole_trace("finish, type=%d|len=%u", resp->info.type, copy_n); if (send_fn(sock, iov, 2) < 0) { -#ifdef __linux__ - if (errno == ENOBUFS && max_length > SW_BUFFER_SIZE_STD) { + if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_BUFFER_SIZE_STD) { max_length = SW_IPC_BUFFER_SIZE; if (resp->info.flags & SW_EVENT_DATA_END) { resp->info.flags &= ~SW_EVENT_DATA_END; } continue; } -#endif return false; } @@ -270,10 +281,32 @@ bool MessageBus::write(Socket *sock, SendData *resp) { size_t MessageBus::get_memory_size() { size_t size = buffer_size_; - for (auto p : packet_pool_) { + for (auto &p : packet_pool_) { size += p.second->size; } return size; } +void MessageBus::init_pipe_socket(network::Socket *sock) { + int pipe_fd = sock->get_fd(); + if ((size_t) pipe_fd >= pipe_sockets_.size()) { + pipe_sockets_.resize(pipe_fd + 1); + } + auto _socket = make_socket(pipe_fd, SW_FD_PIPE); + _socket->buffer_size = UINT_MAX; + if (!_socket->nonblock) { + _socket->set_nonblock(); + } + pipe_sockets_[pipe_fd] = _socket; +} + +MessageBus::~MessageBus() { + for (auto _socket : pipe_sockets_) { + if (_socket) { + _socket->fd = -1; + _socket->free(); + } + } +} + } // namespace swoole diff --git a/src/protocol/mime_type.cc b/src/protocol/mime_type.cc index cf94ff701e4..177606b285a 100644 --- a/src/protocol/mime_type.cc +++ b/src/protocol/mime_type.cc @@ -12,12 +12,14 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_mime_type.h" +#include + namespace swoole { namespace mime_type { @@ -389,7 +391,9 @@ static std::unordered_map map_( static const std::string octet_stream("application/octet-stream"); static std::string get_suffix(const std::string &filename) { - return std::string(filename).substr(filename.find_last_of('.') + 1); + std::string suffix = std::string(filename).substr(filename.find_last_of('.') + 1); + std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); + return suffix; } const std::unordered_map &list() { diff --git a/src/protocol/mqtt.cc b/src/protocol/mqtt.cc index e4c7b85c8ae..f2ce9de04fc 100644 --- a/src/protocol/mqtt.cc +++ b/src/protocol/mqtt.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Author: Xinhua Guo | +----------------------------------------------------------------------+ */ @@ -40,9 +40,9 @@ void set_protocol(Protocol *protocol) { // but there's no chance to read the next mqtt request ,because MQTT client will recv ACK blocking #define SW_MQTT_RECV_LEN_AGAIN 0 -ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *data, uint32_t size) { +ssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { //-1 cause the arg 'size' contain length_offset(1 byte len) - uint32_t recv_variable_header_size = (size - 1); + uint32_t recv_variable_header_size = (pl->buf_size - 1); if (recv_variable_header_size < SW_MQTT_MIN_LENGTH_SIZE) { // recv continue return SW_MQTT_RECV_LEN_AGAIN; } @@ -53,7 +53,7 @@ ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *data, u ssize_t variable_header_byte_count = 0; while (1) { variable_header_byte_count++; - byte = data[variable_header_byte_count]; + byte = pl->buf[variable_header_byte_count]; length += (byte & 127) * mul; mul *= 128; if ((byte & 128) == 0) { // done! there is no surplus length byte diff --git a/src/protocol/redis.cc b/src/protocol/redis.cc index 424319a849b..c625f0c717a 100644 --- a/src/protocol/redis.cc +++ b/src/protocol/redis.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -43,7 +43,7 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { int ret; char *buf_ptr; size_t buf_size; - + RecvData rdata{}; Request *request; network::Socket *socket = conn->socket; @@ -65,7 +65,7 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { int n = socket->recv(buf_ptr, buf_size, 0); if (n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from socket#%d failed", conn->fd); return SW_OK; @@ -81,7 +81,7 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { if (strncmp(buffer->str + buffer->length - SW_CRLF_LEN, SW_CRLF, SW_CRLF_LEN) != 0) { if (buffer->size < protocol->package_max_length) { - uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); + uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize()); if (extend_size > protocol->package_max_length) { extend_size = protocol->package_max_length; } @@ -148,7 +148,9 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { buffer->offset = buffer->length; if (request->n_lines_received == request->n_lines_total) { - if (protocol->onPackage(protocol, socket, buffer->str, buffer->length) < 0) { + rdata.info.len = buffer->length; + rdata.data = buffer->str; + if (protocol->onPackage(protocol, socket, &rdata) < 0) { return SW_ERR; } if (socket->removed) { diff --git a/src/protocol/socks5.cc b/src/protocol/socks5.cc index fd25bfbf946..f58939666c8 100644 --- a/src/protocol/socks5.cc +++ b/src/protocol/socks5.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/ssl.cc b/src/protocol/ssl.cc index 0c132468952..72cd5592d24 100644 --- a/src/protocol/ssl.cc +++ b/src/protocol/ssl.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,9 +22,9 @@ #ifdef SW_USE_OPENSSL +using swoole::SSLContext; using swoole::network::Address; using swoole::network::Socket; -using swoole::SSLContext; #if OPENSSL_VERSION_NUMBER < 0x10000000L #error "require openssl version 1.0 or later" @@ -56,11 +56,7 @@ static int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_l #endif std::string swoole_ssl_get_version_message() { - std::string message = swoole::std_string::format( - "OPENSSL_VERSION: %s\n", - OPENSSL_VERSION_TEXT); - - return message; + return swoole::std_string::format("OPENSSL_VERSION: %s\n", OPENSSL_VERSION_TEXT); } static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line); @@ -224,10 +220,10 @@ namespace swoole { #ifndef OPENSSL_NO_NEXTPROTONEG -const std::string HTTP2_H2_ALPN("\x2h2"); -const std::string HTTP2_H2_16_ALPN("\x5h2-16"); -const std::string HTTP2_H2_14_ALPN("\x5h2-14"); -const std::string HTTP1_NPN("\x08http/1.1"); +#define HTTP2_H2_ALPN "\x02h2" +#define HTTP2_H2_16_ALPN "\x05h2-16" +#define HTTP2_H2_14_ALPN "\x05h2-14" +#define HTTP1_NPN "\x08http/1.1" static bool ssl_select_proto(const uchar **out, uchar *outlen, const uchar *in, uint inlen, const std::string &key) { for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) { @@ -246,24 +242,22 @@ static bool ssl_select_h2(const uchar **out, uchar *outlen, const uchar *in, uin ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_14_ALPN); } - #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation -static int ssl_alpn_advertised( - SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; +static int ssl_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) { + unsigned int protos_len; + const char *protos; - std::string value; - SSLContext *cfg = (SSLContext*) arg; + SSLContext *cfg = (SSLContext *) arg; if (cfg->http_v2) { - value = HTTP2_H2_ALPN + HTTP1_NPN; + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } else { - value = HTTP1_NPN; + protos = HTTP1_NPN; + protos_len = sizeof(HTTP1_NPN) - 1; } - srv = (unsigned char *) value.c_str(); - srvlen = value.length(); - if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen, in, inlen) != OPENSSL_NPN_NEGOTIATED) { + if (SSL_select_next_proto((unsigned char **) out, outlen, (const uchar *) protos, protos_len, in, inlen) != + OPENSSL_NPN_NEGOTIATED) { return SSL_TLSEXT_ERR_NOACK; } return SSL_TLSEXT_ERR_OK; @@ -279,19 +273,19 @@ static int ssl_select_next_proto_cb(SSL *ssl, uchar **out, uchar *outlen, const swoole_trace_log(SW_TRACE_HTTP2, "[NPN] server offers: %s", info.c_str()); #endif SSLContext *ctx = (SSLContext *) arg; - if (ctx->http_v2 && !ssl_select_h2(const_cast(out), outlen, in, inlen)) { + if (ctx->http_v2 && !ssl_select_h2(const_cast(out), outlen, in, inlen)) { swoole_warning("HTTP/2 protocol was not selected, expects [h2]"); return SSL_TLSEXT_ERR_NOACK; } else if (ctx->http) { - *out = (uchar*) HTTP1_NPN.c_str(); - *outlen = HTTP1_NPN.length(); + *out = (uchar *) HTTP1_NPN; + *outlen = sizeof(HTTP1_NPN) - 1; } return SSL_TLSEXT_ERR_OK; } #endif static int ssl_passwd_callback(char *buf, int num, int verify, void *data) { - SSLContext *ctx = (SSLContext*) data; + SSLContext *ctx = (SSLContext *) data; if (!ctx->passphrase.empty()) { int len = ctx->passphrase.length(); if (len < num - 1) { @@ -432,8 +426,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_certificate_file(context, cert_file.c_str(), SSL_FILETYPE_PEM) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_certificate_file(%s) failed, Error: %s[%d]", cert_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_certificate_file(%s) failed, Error: %s[%d]", + cert_file.c_str(), + ERR_reason_error_string(error), + error); return true; } /* @@ -442,8 +438,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_certificate_chain_file(context, cert_file.c_str()) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_certificate_chain_file(%s) failed, Error: %s[%d]", cert_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_certificate_chain_file(%s) failed, Error: %s[%d]", + cert_file.c_str(), + ERR_reason_error_string(error), + error); return false; } } @@ -453,8 +451,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_PrivateKey_file(context, key_file.c_str(), SSL_FILETYPE_PEM) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_PrivateKey_file(%s) failed, Error: %s[%d]", key_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_PrivateKey_file(%s) failed, Error: %s[%d]", + key_file.c_str(), + ERR_reason_error_string(error), + error); return false; } /* @@ -468,8 +468,10 @@ bool SSLContext::create() { #ifdef SW_SUPPORT_DTLS if (protocols & SW_SSL_DTLS) { +#ifndef OPENSSL_IS_BORINGSSL SSL_CTX_set_cookie_generate_cb(context, swoole_ssl_generate_cookie); SSL_CTX_set_cookie_verify_cb(context, swoole_ssl_verify_cookie); +#endif } #endif @@ -481,16 +483,19 @@ bool SSLContext::create() { #if OPENSSL_VERSION_NUMBER >= 0x10002000L if (http || http_v2) { - std::string value; + unsigned int protos_len; + const char *protos; if (http_v2) { - value = HTTP2_H2_ALPN + HTTP1_NPN; + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } else { - value = HTTP1_NPN; + protos = HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } #ifndef OPENSSL_NO_NEXTPROTONEG SSL_CTX_set_next_proto_select_cb(context, ssl_select_next_proto_cb, nullptr); #endif - if (SSL_CTX_set_alpn_protos(context, (const unsigned char *) value.c_str(), value.length()) < 0) { + if (SSL_CTX_set_alpn_protos(context, (const uchar *) protos, protos_len) < 0) { return false; } @@ -504,6 +509,10 @@ bool SSLContext::create() { } #endif +#ifdef OPENSSL_IS_BORINGSSL + SSL_CTX_set_grease_enabled(context, grease); +#endif + if (!client_cert_file.empty() && !set_client_certificate()) { swoole_warning("set_client_certificate() error"); return false; @@ -691,7 +700,7 @@ SSLContext::~SSLContext() { SSL_CTX_free(context); } -} +} // namespace swoole static int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { #if 0 diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 606c291184f..0b2cc4f2d57 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -54,48 +54,51 @@ static inline uint16_t get_ext_flags(uchar opcode, uchar flags) { | Payload Data continued ... | +---------------------------------------------------------------+ */ - -ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *buf, uint32_t length) { +static ssize_t get_package_length_impl(PacketLength *pl) { // need more data - if (length < SW_WEBSOCKET_HEADER_LEN) { + if (pl->buf_size < SW_WEBSOCKET_HEADER_LEN) { return 0; } - + const char *buf = pl->buf; char mask = (buf[1] >> 7) & 0x1; // 0-125 uint64_t payload_length = buf[1] & 0x7f; - size_t header_length = SW_WEBSOCKET_HEADER_LEN; + pl->header_len = SW_WEBSOCKET_HEADER_LEN; buf += SW_WEBSOCKET_HEADER_LEN; // uint16_t, 2byte if (payload_length == SW_WEBSOCKET_EXT16_LENGTH) { - header_length += sizeof(uint16_t); - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += sizeof(uint16_t); + if (pl->buf_size < pl->header_len) { return 0; } payload_length = ntohs(*((uint16_t *) buf)); - buf += sizeof(uint16_t); } // uint64_t, 8byte else if (payload_length == SW_WEBSOCKET_EXT64_LENGTH) { - header_length += sizeof(uint64_t); - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += sizeof(uint64_t); + if (pl->buf_size < pl->header_len) { return 0; } payload_length = swoole_ntoh64(*((uint64_t *) buf)); - buf += sizeof(uint64_t); } if (mask) { - header_length += SW_WEBSOCKET_MASK_LEN; - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += SW_WEBSOCKET_MASK_LEN; + if (pl->buf_size < pl->header_len) { return 0; } } - swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%zu, payload_length=%lu", header_length, payload_length); - return header_length + payload_length; + if ((ssize_t) payload_length < 0) { + return -1; + } + swoole_trace_log( + SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%" PRIu64, pl->header_len, payload_length); + + return (ssize_t) pl->header_len + (ssize_t) payload_length; +} + +ssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { + return get_package_length_impl(pl); } static sw_inline void mask(char *data, size_t len, const char *mask_key) { @@ -124,9 +127,9 @@ bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_ header->MASK = !!(_flags & FLAG_MASK); pos = 2; - if (length < 126) { + if (length < SW_WEBSOCKET_EXT16_LENGTH) { header->LENGTH = length; - } else if (length < 65536) { + } else if (length <= SW_WEBSOCKET_EXT16_MAX_LEN) { header->LENGTH = SW_WEBSOCKET_EXT16_LENGTH; uint16_t *length_ptr = (uint16_t *) (frame_header + pos); *length_ptr = htons(length); @@ -162,48 +165,44 @@ bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_ } bool decode(Frame *frame, char *data, size_t length) { - memcpy(frame, data, SW_WEBSOCKET_HEADER_LEN); - - // 0-125 - size_t payload_length = frame->header.LENGTH; - uint8_t header_length = SW_WEBSOCKET_HEADER_LEN; - char *buf = data + SW_WEBSOCKET_HEADER_LEN; - - // uint16_t, 2byte - if (frame->header.LENGTH == 0x7e) { - payload_length = ntohs(*((uint16_t *) buf)); - header_length += 2; - } - // uint64_t, 8byte - else if (frame->header.LENGTH > 0x7e) { - payload_length = swoole_ntoh64(*((uint64_t *) buf)); - header_length += 8; + frame->header.OPCODE = data[0] & 0xf; + frame->header.RSV1 = (data[0] >> 6) & 0x1; + frame->header.RSV2 = (data[0] >> 5) & 0x1; + frame->header.RSV3 =(data[0] >> 4) & 0x1; + frame->header.FIN = (data[0] >> 7) & 0x1; + frame->header.MASK = (data[1] >> 7) & 0x1; + frame->header.LENGTH = data[1] & 0x7f; + + PacketLength pl{data, (uint32_t) length, 0}; + ssize_t total_length = get_package_length_impl(&pl); + if (total_length <= 0 || length < (size_t) total_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET, + "incomplete packet, expected length is %zu, actual length is %zu", + total_length, + length); + return false; } - swoole_trace_log(SW_TRACE_WEBSOCKET, - "decode frame, payload_length=%ld, mask=%d, opcode=%d", - payload_length, - frame->header.MASK, - frame->header.OPCODE); - - if (payload_length == 0) { - frame->header_length = header_length; - frame->payload_length = 0; - frame->payload = nullptr; + frame->payload_length = total_length - pl.header_len; + frame->header_length = pl.header_len; - return true; - } + swoole_trace_log(SW_TRACE_WEBSOCKET, + "decode frame, payload_length=%ld, mask=%d, opcode=%d", + frame->payload_length, + frame->header.MASK, + frame->header.OPCODE); - if (frame->header.MASK) { - memcpy(frame->mask_key, data + header_length, SW_WEBSOCKET_MASK_LEN); - header_length += SW_WEBSOCKET_MASK_LEN; - mask(data + header_length, payload_length, frame->mask_key); + if (frame->payload_length == 0) { + frame->payload = nullptr; + } else { + frame->payload = data + frame->header_length; + if (frame->header.MASK) { + memcpy(frame->mask_key, frame->payload - SW_WEBSOCKET_MASK_LEN, SW_WEBSOCKET_MASK_LEN); + mask(frame->payload, frame->payload_length, frame->mask_key); + } } - frame->payload = data + header_length; - frame->header_length = header_length; - frame->payload_length = payload_length; - return true; } @@ -220,7 +219,9 @@ int pack_close_frame(String *buffer, int code, char *reason, size_t length, uint memcpy(payload + SW_WEBSOCKET_CLOSE_CODE_LEN, reason, length); } flags |= FLAG_FIN; - encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags); + if (!encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags)) { + return SW_ERR; + } return SW_OK; } @@ -239,17 +240,21 @@ void print_frame(Frame *frame) { } } -int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t length) { +int dispatch_frame(const Protocol *proto, Socket *_socket, const RecvData *rdata) { Server *serv = (Server *) proto->private_data_2; Connection *conn = (Connection *) _socket->object; - - String send_frame = {}; + RecvData dispatch_data{}; + String send_frame{}; + const char *data = rdata->data; + const uint32_t length = rdata->info.len; char buf[SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_CLOSE_CODE_LEN + SW_WEBSOCKET_CLOSE_REASON_MAX_LEN]; send_frame.str = buf; send_frame.size = sizeof(buf); Frame ws; - decode(&ws, const_cast(data), length); + if (!decode(&ws, const_cast(data), length)) { + return SW_ERR; + } String *frame_buffer; int frame_length; @@ -275,9 +280,10 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t frame_buffer->append(data + offset, frame_length); // frame is finished, do dispatch if (ws.header.FIN) { - proto->ext_flags = conn->websocket_buffer->offset; - proto->ext_flags |= FLAG_FIN; - Server::dispatch_task(proto, _socket, frame_buffer->str, frame_buffer->length); + dispatch_data.info.ext_flags = conn->websocket_buffer->offset | FLAG_FIN; + dispatch_data.info.len = frame_buffer->length; + dispatch_data.data = frame_buffer->str; + Server::dispatch_task(proto, _socket, &dispatch_data); delete frame_buffer; conn->websocket_buffer = nullptr; } @@ -286,19 +292,21 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t case OPCODE_TEXT: case OPCODE_BINARY: { offset = length - ws.payload_length; - proto->ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); - + int ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); if (!ws.header.FIN) { if (conn->websocket_buffer) { swoole_warning("merging incomplete frame, bad request. remote_addr=%s:%d", - conn->info.get_ip(), - conn->info.get_port()); + conn->info.get_ip(), + conn->info.get_port()); return SW_ERR; } conn->websocket_buffer = new swoole::String(data + offset, length - offset); - conn->websocket_buffer->offset = proto->ext_flags; + conn->websocket_buffer->offset = ext_flags; } else { - Server::dispatch_task(proto, _socket, data + offset, length - offset); + dispatch_data.info.ext_flags = ext_flags; + dispatch_data.info.len = length - offset; + dispatch_data.data = data + offset; + Server::dispatch_task(proto, _socket, &dispatch_data); } break; } @@ -306,20 +314,20 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t case OPCODE_PONG: if (length >= (sizeof(buf) - SW_WEBSOCKET_HEADER_LEN)) { swoole_warning("%s frame application data is too big. remote_addr=%s:%d", - ws.header.OPCODE == OPCODE_PING ? "ping" : "pong", - conn->info.get_ip(), - conn->info.get_port()); + ws.header.OPCODE == OPCODE_PING ? "ping" : "pong", + conn->info.get_ip(), + conn->info.get_port()); return SW_ERR; } else if (length == SW_WEBSOCKET_HEADER_LEN) { - data = nullptr; - length = 0; + dispatch_data.data = nullptr; + dispatch_data.info.len = 0; } else { offset = ws.header.MASK ? SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN : SW_WEBSOCKET_HEADER_LEN; - data += offset; - length -= offset; + dispatch_data.info.len = length - offset; + dispatch_data.data = dispatch_data.info.len == 0 ? nullptr : data + offset; } - proto->ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); - Server::dispatch_task(proto, _socket, data, length); + dispatch_data.info.ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); + Server::dispatch_task(proto, _socket, &dispatch_data); break; case OPCODE_CLOSE: @@ -330,9 +338,10 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t if (conn->websocket_status != STATUS_CLOSING) { // Dispatch the frame with the same format of message frame offset = length - ws.payload_length; - proto->ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); - - Server::dispatch_task(proto, _socket, data + offset, length - offset); + dispatch_data.info.ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); + dispatch_data.info.len = length - offset; + dispatch_data.data = data + offset; + Server::dispatch_task(proto, _socket, &dispatch_data); // Client attempt to close send_frame.str[0] = 0x88; // FIN | OPCODE: WEBSOCKET_OPCODE_CLOSE diff --git a/src/reactor/base.cc b/src/reactor/base.cc index d4be6cba97e..bc3c1bc658e 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -48,7 +48,7 @@ ReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events); ReactorImpl *make_reactor_select(Reactor *_reactor); -void ReactorImpl::after_removal_failure(network::Socket *_socket) { +void ReactorImpl::after_removal_failure(Socket *_socket) { if (!_socket->silent_remove) { swoole_sys_warning("failed to delete events[fd=%d#%d, type=%d, events=%d]", _socket->fd, @@ -110,7 +110,7 @@ Reactor::Reactor(int max_event, Type _type) { default_write_handler = _writable_callback; - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE]) { + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, this); } @@ -246,7 +246,7 @@ static ssize_t write_func( } else { goto _alloc_buffer; } - } else if (socket->catch_error(errno) == SW_WAIT) { + } else if (socket->catch_write_error(errno) == SW_WAIT) { _alloc_buffer: if (!socket->out_buffer) { buffer = new Buffer(socket->chunk_size); @@ -297,7 +297,7 @@ ssize_t Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_ return write_func(reactor, socket, n, send_fn, append_fn); } -ssize_t Reactor::_writev(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt) { +ssize_t Reactor::_writev(Reactor *reactor, Socket *socket, const iovec *iov, size_t iovcnt) { #ifdef SW_USE_OPENSSL if (socket->ssl) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "does not support SSL"); @@ -359,13 +359,13 @@ int Reactor::_writable_callback(Reactor *reactor, Event *ev) { return SW_OK; } -void Reactor::drain_write_buffer(swSocket *socket) { +void Reactor::drain_write_buffer(Socket *socket) { Event event = {}; event.socket = socket; event.fd = socket->fd; while (!Buffer::empty(socket->out_buffer)) { - if (socket->wait_event(network::Socket::default_write_timeout, SW_EVENT_WRITE) == SW_ERR) { + if (socket->wait_event(Socket::default_write_timeout, SW_EVENT_WRITE) == SW_ERR) { break; } _writable_callback(this, &event); @@ -383,6 +383,10 @@ void Reactor::set_end_callback(enum EndCallback id, const std::function &fn) { exit_conditions[id] = fn; } @@ -404,7 +408,7 @@ Reactor::~Reactor() { destroyed = true; destroy_callbacks.execute(); delete impl; - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_DESTROY]) { + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY)) { swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, this); } } diff --git a/src/reactor/epoll.cc b/src/reactor/epoll.cc index 9beb4c89e94..f0b9114523d 100644 --- a/src/reactor/epoll.cc +++ b/src/reactor/epoll.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/kqueue.cc b/src/reactor/kqueue.cc index e2cffd89199..98f90226f6d 100644 --- a/src/reactor/kqueue.cc +++ b/src/reactor/kqueue.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -26,6 +26,9 @@ #else #include #endif +#ifdef __NetBSD__ +#include +#endif namespace swoole { @@ -99,7 +102,7 @@ int ReactorKqueue::add(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); @@ -141,7 +144,7 @@ int ReactorKqueue::set(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); @@ -193,7 +196,7 @@ int ReactorKqueue::del(Socket *socket) { int ret; int fd = socket->fd; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); diff --git a/src/reactor/poll.cc b/src/reactor/poll.cc index cea35d857f7..1fc15e98a97 100644 --- a/src/reactor/poll.cc +++ b/src/reactor/poll.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/select.cc b/src/reactor/select.cc index 1f4dab6a23f..71ec4d146c5 100644 --- a/src/reactor/select.cc +++ b/src/reactor/select.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/base.cc b/src/server/base.cc index c23fb5d9f90..5655186ddaa 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -18,8 +18,44 @@ namespace swoole { +Factory *Server::create_base_factory() { + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + + for (auto port : ports) { + port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (port->gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + } + + return new BaseFactory(this); +} + +void Server::destroy_base_factory() { + sw_free(connection_list); + sw_shm_free((void *) gs->connection_nums); + for (auto port : ports) { + sw_shm_free((void *) port->gs->connection_nums); + } + gs->connection_nums = nullptr; +} + +BaseFactory::BaseFactory(Server *server) : Factory(server) {} + +BaseFactory::~BaseFactory() {} + bool BaseFactory::start() { - SwooleWG.run_always = true; return true; } @@ -54,8 +90,9 @@ bool BaseFactory::dispatch(SendData *task) { } } - server_->message_bus.pass(task); - server_->worker_accept_event(&server_->message_bus.get_buffer()->info); + auto bus = server_->get_worker_message_bus(); + bus->pass(task); + server_->worker_accept_event(&bus->get_buffer()->info); return true; } @@ -89,24 +126,24 @@ bool BaseFactory::end(SessionId session_id, int flags) { _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - _send.info.reactor_id = SwooleG.process_id; + _send.info.reactor_id = swoole_get_process_id(); Session *session = server_->get_session(session_id); if (!session->fd) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "failed to close connection, session#%ld does not exist", session_id); return false; } - if (session->reactor_id != SwooleG.process_id) { - Worker *worker = server_->get_worker(session->reactor_id); - if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { - swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); - return false; - } - return true; + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); + return forward_message(session, &_send); } Connection *conn = server_->get_connection_verify_no_ssl(session_id); @@ -127,7 +164,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; } else if (!(conn->close_force || conn->close_reset) && conn->closed) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; } @@ -146,18 +183,18 @@ bool BaseFactory::end(SessionId session_id, int flags) { conn->closing = 0; conn->closed = 1; conn->close_errno = 0; + network::Socket *_socket = conn->socket; - if (conn->socket == nullptr) { + if (_socket == nullptr) { swoole_warning("session#%ld->socket is nullptr", session_id); return false; } - if (Buffer::empty(conn->socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) { + if (Buffer::empty(_socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) { Reactor *reactor = SwooleTG.reactor; - return Server::close_connection(reactor, conn->socket) == SW_OK; + return Server::close_connection(reactor, _socket) == SW_OK; } else { - BufferChunk *chunk = conn->socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); - chunk->value.data.val1 = _send.info.type; + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); conn->close_queued = 1; return true; } @@ -167,33 +204,40 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != SwooleG.process_id) { - swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, SwooleG.process_id); - Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); - EventData proxy_msg{}; - - if (data->info.type == SW_SERVER_EVENT_SEND_DATA) { - if (!server_->message_bus.write(worker->pipe_master, data)) { - swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); - return false; - } - swoole_trace( - "proxy message, fd=%d, len=%ld", worker->pipe_master->fd, sizeof(proxy_msg.info) + proxy_msg.info.len); - } else if (data->info.type == SW_SERVER_EVENT_SEND_FILE) { - memcpy(&proxy_msg.info, &data->info, sizeof(proxy_msg.info)); - memcpy(proxy_msg.data, data->data, data->info.len); - size_t __len = sizeof(proxy_msg.info) + proxy_msg.info.len; - return worker->pipe_master->send_async((const char *) &proxy_msg, __len); + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); + + if (data->info.type == SW_SERVER_EVENT_SEND_DATA || data->info.type == SW_SERVER_EVENT_SEND_FILE) { + return forward_message(session, data); } else { - swoole_warning("unkown event type[%d]", data->info.type); + swoole_warning("unknown event type[%d]", data->info.type); return false; } - return true; } else { return server_->send_to_connection(data) == SW_OK; } } -BaseFactory::~BaseFactory() {} +bool BaseFactory::forward_message(Session *session, SendData *data) { + Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); + swoole_trace_log(SW_TRACE_SERVER, + "fd=%d, worker_id=%d, type=%d, len=%ld", + worker->pipe_master->get_fd(), + session->reactor_id, + data->info.type, + data->info.len); + + auto mb = server_->get_worker_message_bus(); + auto sock = server_->is_thread_mode() ? mb->get_pipe_socket(worker->pipe_master) : worker->pipe_master; + if (!mb->write(sock, data)) { + swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); + return false; + } + return true; +} } // namespace swoole diff --git a/src/server/manager.cc b/src/server/manager.cc index 3879c7662a0..5bdea0ec9fc 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -40,8 +40,9 @@ struct Manager { std::vector kill_workers; - void start(Server *_server); + void wait(Server *_server); void add_timeout_killer(Worker *workers, int n); + void terminate_all_worker(); static void signal_handler(int sig); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -50,7 +51,7 @@ struct Manager { void Manager::timer_callback(Timer *timer, TimerNode *tnode) { Server *serv = (Server *) tnode->data; - if (serv->hooks[Server::HOOK_MANAGER_TIMER]) { + if (serv->isset_hook(Server::HOOK_MANAGER_TIMER)) { serv->call_hook(Server::HOOK_MANAGER_TIMER, serv); } } @@ -96,128 +97,74 @@ void Manager::add_timeout_killer(Worker *workers, int n) { swoole_timer_after((long) (server_->max_wait_time * 2 * 1000), kill_timeout_process, _list); } -// create worker child proccess int Server::start_manager_process() { - pid_t pid; - - if (task_worker_num > 0) { - if (create_task_workers() < 0) { - return SW_ERR; - } - - Worker *worker; - SW_LOOP_N(task_worker_num) { - worker = &gs->task_workers.workers[i]; - create_worker(worker); - if (task_ipc_mode == TASK_IPC_UNIXSOCK) { - store_pipe_fd(worker->pipe_object); - } - } + SW_LOOP_N(worker_num) { + create_worker(get_worker(i)); } - // User Worker Process - if (get_user_worker_num() > 0) { - if (create_user_workers() < 0) { - return SW_ERR; - } - - int i = 0; - for (auto worker : user_worker_list) { - memcpy(&user_workers[i], worker, sizeof(user_workers[i])); - create_worker(worker); - i++; - } + if (gs->event_workers.create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) { + return SW_ERR; } - if (gs->event_workers.create_message_box(65536) == SW_ERR) { + if (task_worker_num > 0 && create_task_workers() < 0) { return SW_ERR; } - pid = swoole_fork(0); - switch (pid) { - // fork manager process - case 0: { - // wait master process - if (!is_started()) { - swoole_error("master process is not running"); - return SW_ERR; - } + if (get_user_worker_num() > 0 && create_user_workers() < 0) { + return SW_ERR; + } - pid_t pid; + auto fn = [this](void) { + swoole_set_process_type(SW_PROCESS_MANAGER); + gs->manager_pid = SwooleG.pid = getpid(); if (task_worker_num > 0) { if (gs->task_workers.start() == SW_ERR) { - swoole_error("failed to start task workers"); - return SW_ERR; + swoole_sys_error("failed to start task worker"); + return; } } SW_LOOP_N(worker_num) { Worker *worker = get_worker(i); - pid = spawn_event_worker(worker); - if (pid < 0) { - swoole_error("fork() failed"); - return SW_ERR; - } else { - worker->pid = pid; + if (factory->spawn_event_worker(worker) < 0) { + swoole_sys_error("failed to fork event worker"); + return; } } if (!user_worker_list.empty()) { for (auto worker : user_worker_list) { - if (worker->pipe_object) { - store_pipe_fd(worker->pipe_object); - } - pid = spawn_user_worker(worker); - if (pid < 0) { - swoole_error("failed to start user workers"); - return SW_ERR; + if (factory->spawn_user_worker(worker) < 0) { + swoole_sys_error("failed to fork user worker"); + return; } } } - SwooleG.process_type = SW_PROCESS_MANAGER; - SwooleG.pid = getpid(); Manager manager{}; - manager.start(this); - exit(0); - break; - } - // master process - default: - gs->event_workers.master_pid = gs->manager_pid = pid; - break; - case -1: - swoole_error("fork() failed"); - return SW_ERR; - } - return SW_OK; -} + manager.wait(this); + }; -void Server::check_worker_exit_status(int worker_id, const ExitStatus &exit_status) { - if (exit_status.get_status() != 0) { - swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" - "%s", - exit_status.get_pid(), - worker_id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); - - if (onWorkerError != nullptr) { - onWorkerError(this, worker_id, exit_status); + if (is_base_mode()) { + fn(); + } else { + if (swoole_fork_exec(fn) < 0) { + swoole_sys_warning("failed fork manager process"); + return SW_ERR; } } + return SW_OK; } -void Manager::start(Server *_server) { +void Manager::wait(Server *_server) { server_ = _server; server_->manager = this; ProcessPool *pool = &server_->gs->event_workers; pool->onWorkerMessage = Server::read_worker_message; + _server->gs->manager_pid = _server->gs->event_workers.master_pid = getpid(); - SwooleG.use_signalfd = 0; SwooleTG.reactor = nullptr; pool->reload_workers = new Worker[_server->worker_num + _server->task_worker_num]; @@ -238,21 +185,18 @@ void Manager::start(Server *_server) { #ifdef SIGRTMIN swoole_signal_set(SIGRTMIN, signal_handler); #endif - // swSignal_set(SIGINT, signal_handler); + + if (_server->is_process_mode()) { #if defined(__linux__) - prctl(PR_SET_PDEATHSIG, SIGTERM); + prctl(PR_SET_PDEATHSIG, SIGTERM); #elif defined(__FreeBSD__) - int sigid = SIGTERM; - procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); -#endif - -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_wait(&_server->gs->manager_barrier); -#else - SW_START_SLEEP; + int sigid = SIGTERM; + procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); #endif + _server->gs->manager_barrier.wait(); + } - if (_server->hooks[Server::HOOK_MANAGER_START]) { + if (_server->isset_hook(Server::HOOK_MANAGER_START)) { _server->call_hook(Server::HOOK_MANAGER_START, _server); } @@ -266,7 +210,7 @@ void Manager::start(Server *_server) { while (_server->running) { ExitStatus exit_status = wait_process(); - + const auto errnoAfterWait = errno; if (pool->read_message) { EventData msg; while (pool->pop_message(&msg, sizeof(msg)) > 0) { @@ -280,27 +224,23 @@ void Manager::start(Server *_server) { WorkerStopMessage worker_stop_msg; memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); if (worker_stop_msg.worker_id >= _server->worker_num) { - _server->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); + _server->factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { Worker *worker = _server->get_worker(worker_stop_msg.worker_id); - pid_t new_pid = _server->spawn_event_worker(worker); - if (new_pid > 0) { - worker->pid = new_pid; - } + _server->factory->spawn_event_worker(worker); } } pool->read_message = false; } - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = 0; - swoole_timer_select(); + if (SwooleTG.timer) { + SwooleTG.timer->select(); } if (exit_status.get_pid() < 0) { if (!pool->reloading) { _error: - if (errno > 0 && errno != EINTR) { + if (errnoAfterWait > 0 && errnoAfterWait != EINTR) { swoole_sys_warning("wait() failed"); } continue; @@ -331,7 +271,8 @@ void Manager::start(Server *_server) { if (_server->reload_async) { SW_LOOP_N(_server->worker_num) { if (swoole_kill(pool->reload_workers[i].pid, SIGTERM) < 0) { - swoole_sys_warning("swKill(%d, SIGTERM) [%d] failed", pool->reload_workers[i].pid, i); + swoole_sys_warning( + "failed to kill(%d, SIGTERM) worker#[%d]", pool->reload_workers[i].pid, i); } } pool->reload_worker_i = _server->worker_num; @@ -370,33 +311,29 @@ void Manager::start(Server *_server) { if (_server->running) { // event workers SW_LOOP_N(_server->worker_num) { + Worker *worker = _server->get_worker(i); // find worker - if (exit_status.get_pid() != _server->workers[i].pid) { + if (exit_status.get_pid() != worker->pid) { continue; } // check the process return code and signal - _server->check_worker_exit_status(i, exit_status); + _server->factory->check_worker_exit_status(worker, exit_status); - while (1) { - Worker *worker = _server->get_worker(i); - pid_t new_pid = _server->spawn_event_worker(worker); - if (new_pid < 0) { + do { + if (_server->factory->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; - } else { - worker->pid = new_pid; - break; } - } + } while (0); } // task worker if (_server->gs->task_workers.map_) { auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != _server->gs->task_workers.map_->end()) { - _server->check_worker_exit_status(iter->second->id, exit_status); - _server->spawn_task_worker(iter->second); + _server->factory->check_worker_exit_status(iter->second, exit_status); + _server->factory->spawn_task_worker(iter->second); } } // user process @@ -447,7 +384,7 @@ void Manager::start(Server *_server) { } } if (!_server->user_worker_map.empty()) { - for (auto kv : _server->user_worker_map) { + for (auto &kv : _server->user_worker_map) { kill_workers.push_back(kv.second->pid); } } @@ -456,9 +393,9 @@ void Manager::start(Server *_server) { */ alarm(_server->max_wait_time * 2); } - _server->kill_event_workers(); - _server->kill_task_workers(); - _server->kill_user_workers(); + _server->factory->kill_event_workers(); + _server->factory->kill_task_workers(); + _server->factory->kill_user_workers(); // force kill if (_server->max_wait_time) { alarm(0); @@ -468,7 +405,15 @@ void Manager::start(Server *_server) { } } -void Manager::signal_handler(int sig) { +void Manager::terminate_all_worker() { + // clear the timer + alarm(0); + for (auto i = kill_workers.begin(); i != kill_workers.end(); i++) { + swoole_kill(*i, SIGKILL); + } +} + +void Manager::signal_handler(int signo) { Server *_server = sw_server(); if (!_server || !_server->manager) { return; @@ -476,43 +421,26 @@ void Manager::signal_handler(int sig) { Manager *manager = _server->manager; ProcessPool *pool = &_server->gs->event_workers; - switch (sig) { + switch (signo) { case SIGTERM: _server->running = false; break; - /** - * reload all workers - */ case SIGUSR1: - if (pool->reload()) { - manager->reload_all_worker = true; - } - sw_logger()->reopen(); - break; - /** - * only reload task workers - */ case SIGUSR2: - if (pool->reload()) { - manager->reload_task_worker = true; - } + _server->reload(signo == SIGUSR1); sw_logger()->reopen(); break; case SIGIO: pool->read_message = true; break; case SIGALRM: - SwooleG.signal_alarm = 1; if (manager->force_kill) { - alarm(0); - for (auto i = manager->kill_workers.begin(); i != manager->kill_workers.end(); i++) { - kill(*i, SIGKILL); - } + manager->terminate_all_worker(); } break; default: #ifdef SIGRTMIN - if (sig == SIGRTMIN) { + if (signo == SIGRTMIN) { sw_logger()->reopen(); } #endif @@ -548,16 +476,16 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) return SW_ERR; } while (0); - serv->check_worker_exit_status(exit_worker->id, exit_status); + serv->factory->check_worker_exit_status(exit_worker, exit_status); pid_t new_process_pid = -1; switch (worker_type) { case SW_PROCESS_TASKWORKER: - new_process_pid = serv->spawn_task_worker(exit_worker); + new_process_pid = serv->factory->spawn_task_worker(exit_worker); break; case SW_PROCESS_USERWORKER: - new_process_pid = serv->spawn_user_worker(exit_worker); + new_process_pid = serv->factory->spawn_user_worker(exit_worker); break; default: /* never here */ @@ -598,110 +526,31 @@ void Server::read_worker_message(ProcessPool *pool, EventData *msg) { serv->message_bus.write(serv->get_command_reply_socket(), &task); } -/** - * kill and wait all user process - */ -void Server::kill_user_workers() { - if (user_worker_map.empty()) { - return; - } - - for (auto &kv : user_worker_map) { - swoole_kill(kv.second->pid, SIGTERM); - } - - for (auto &kv : user_worker_map) { - int __stat_loc; - if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", kv.second->pid); - } - } -} - -/** - * kill and wait all child process - */ -void Server::kill_event_workers() { - int status; - - if (worker_num == 0) { - return; +bool Server::reload(bool reload_all_workers) { + if (is_thread_mode()) { + return reload_worker_threads(reload_all_workers); } - SW_LOOP_N(worker_num) { - swoole_trace("[Manager]kill worker processor"); - swoole_kill(workers[i].pid, SIGTERM); - } - SW_LOOP_N(worker_num) { - if (swoole_waitpid(workers[i].pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", workers[i].pid); - } + if (gs->manager_pid == 0) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not supported with single process mode"); + return false; } -} -/** - * kill and wait task process - */ -void Server::kill_task_workers() { - if (task_worker_num == 0) { - return; + if (getpid() != gs->manager_pid) { + return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) == 0; } - gs->task_workers.shutdown(); -} - -pid_t Server::spawn_event_worker(Worker *worker) { - pid_t pid; - - pid = swoole_fork(0); - // fork() failed - if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); - return SW_ERR; - } - // worker child processor - else if (pid == 0) { - exit(start_event_worker(worker)); + ProcessPool *pool = &gs->event_workers; + if (!pool->reload()) { + return false; } - // parent,add to writer - else { - return pid; - } -} - -pid_t Server::spawn_user_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); - return SW_ERR; - } - // child - else if (pid == 0) { - SwooleG.process_type = SW_PROCESS_USERWORKER; - SwooleG.process_id = worker->id; - SwooleWG.worker = worker; - worker->pid = getpid(); - onUserWorkerStart(this, worker); - exit(0); + if (reload_all_workers) { + manager->reload_all_worker = true; + } else { + manager->reload_task_worker = true; } - // parent - else { - if (worker->pid) { - user_worker_map.erase(worker->pid); - } - /** - * worker: local memory - * user_workers: shared memory - */ - get_worker(worker->id)->pid = worker->pid = pid; - user_worker_map.emplace(std::make_pair(pid, worker)); - return pid; - } -} - -pid_t Server::spawn_task_worker(Worker *worker) { - return gs->task_workers.spawn(worker); + return true; } } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index dfc6131343c..b7cfa038d6e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,8 +29,6 @@ swoole::Server *g_server_instance = nullptr; namespace swoole { -static void Server_signal_handler(int sig); - TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) { return [this, port, conn, reactor](Timer *, TimerNode *) { if (conn->protect) { @@ -55,7 +53,7 @@ TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, C void Server::disable_accept() { enable_accept_timer = swoole_timer_add( - SW_ACCEPT_RETRY_TIME * 1000, + SW_ACCEPT_RETRY_TIME, false, [](Timer *timer, TimerNode *tnode) { Server *serv = (Server *) tnode->data; @@ -81,35 +79,26 @@ void Server::disable_accept() { } } -void Server::close_port(bool only_stream_port) { - for (auto port : ports) { - if (only_stream_port && port->is_dgram()) { - continue; - } - if (port->socket) { - port->socket->free(); - port->socket = nullptr; - } - } -} - void Server::call_command_callback(int64_t request_id, const std::string &result) { auto iter = command_callbacks.find(request_id); if (iter == command_callbacks.end()) { - swoole_error_log( - SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Invalid command result[request_id=%ld]", request_id); + swoole_error_log(SW_LOG_ERROR, + SW_ERROR_SERVER_INVALID_COMMAND, + "Invalid command result[request_id=%" PRId64 "]", + request_id); return; } iter->second(this, result); + command_callbacks.erase(request_id); } -ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { +void Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { PipeBuffer *buffer = mb.get_buffer(); int command_id = buffer->info.server_fd; auto iter = command_handlers.find(command_id); if (iter == command_handlers.end()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[command_id=%d]", command_id); - return SW_OK; + return; } Server::Command::Handler handler = iter->second; @@ -124,7 +113,7 @@ ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Sock task.info.len = result.length(); task.data = result.c_str(); - return mb.write(sock, &task) ? SW_OK : SW_ERR; + mb.write(sock, &task); } std::string Server::call_command_handler_in_master(int command_id, const std::string &msg) { @@ -157,7 +146,7 @@ int Server::accept_command_result(Reactor *reactor, Event *event) { int Server::accept_connection(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - ListenPort *listen_host = serv->get_port_by_server_fd(event->fd); + ListenPort *listen_host = (ListenPort *) event->socket->object; for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) { Socket *sock = event->socket->accept(); @@ -219,6 +208,7 @@ int Server::accept_connection(Reactor *reactor, Event *event) { ev.type = SW_SERVER_EVENT_INCOMING; ev.fd = conn->session_id; ev.reactor_id = conn->reactor_id; + ev.server_fd = event->fd; if (serv->send_to_reactor_thread((EventData *) &ev, sizeof(ev), conn->session_id) < 0) { serv->abort_connection(reactor, listen_host, sock); return SW_OK; @@ -234,7 +224,7 @@ int Server::connection_incoming(Reactor *reactor, Connection *conn) { if (port->max_idle_time > 0) { auto timeout_callback = get_timeout_callback(port, reactor, conn); conn->socket->recv_timeout_ = port->max_idle_time; - conn->socket->recv_timer = swoole_timer_add(port->max_idle_time * 1000, true, timeout_callback); + conn->socket->recv_timer = swoole_timer_add((long) (port->max_idle_time * 1000), true, timeout_callback); } #ifdef SW_USE_OPENSSL if (conn->socket->ssl) { @@ -340,24 +330,48 @@ void Server::set_max_connection(uint32_t _max_connection) { } } +const char *Server::get_startup_error_message() { + auto error_msg = swoole_get_last_error_msg(); + if (strlen(error_msg) == 0 && swoole_get_last_error() > 0) { + auto buf = sw_tg_buffer(); + buf->clear(); + buf->append(swoole_get_last_error()); + buf->str[buf->length] = '\0'; + error_msg = buf->str; + } + return error_msg; +} + int Server::start_check() { // disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE if (is_process_mode()) { if (!is_support_unsafe_events()) { if (onConnect) { - swoole_warning("cannot set 'onConnect' event when using dispatch_mode=1/3/7"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onConnect' event when using dispatch_mode=%d", + dispatch_mode); onConnect = nullptr; } if (onClose) { - swoole_warning("cannot set 'onClose' event when using dispatch_mode=1/3/7"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onClose' event when using dispatch_mode=%d", + dispatch_mode); onClose = nullptr; } if (onBufferFull) { - swoole_warning("cannot set 'onBufferFull' event when using dispatch_mode=1/3/7"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onBufferFull' event when using dispatch_mode=%d", + dispatch_mode); onBufferFull = nullptr; } if (onBufferEmpty) { - swoole_warning("cannot set 'onBufferEmpty' event when using dispatch_mode=1/3/7"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onBufferEmpty' event when using dispatch_mode=%d", + dispatch_mode); onBufferEmpty = nullptr; } disable_notify = 1; @@ -370,7 +384,7 @@ int Server::start_check() { } if (task_worker_num > 0) { if (onTask == nullptr) { - swoole_warning("onTask event callback must be set"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onTask' callback"); return SW_ERR; } } @@ -389,11 +403,11 @@ int Server::start_check() { ls->protocol.package_max_length = SW_BUFFER_MIN_SIZE; } if (if_require_receive_callback(ls, onReceive != nullptr)) { - swoole_warning("require onReceive callback"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onReceive' callback"); return SW_ERR; } if (if_require_packet_callback(ls, onPacket != nullptr)) { - swoole_warning("require onPacket callback"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onPacket' callback"); return SW_ERR; } if (ls->heartbeat_idle_time > 0) { @@ -407,7 +421,7 @@ int Server::start_check() { /** * OpenSSL thread-safe */ - if (is_process_mode() && !single_thread) { + if ((is_process_mode() && !single_thread) || is_thread_mode()) { swoole_ssl_init_thread_safety(); } #endif @@ -415,18 +429,18 @@ int Server::start_check() { return SW_OK; } -int Server::start_master_thread() { - SwooleTG.type = THREAD_MASTER; - SwooleTG.update_time = 1; - - Reactor *reactor = sw_reactor(); +int Server::start_master_thread(Reactor *reactor) { + swoole_set_thread_type(THREAD_MASTER); + swoole_set_thread_id(single_thread ? 0 : reactor_num); if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) { SwooleTG.timer->reinit(reactor); } + init_signal_handler(); + SwooleG.pid = getpid(); - SwooleG.process_type = SW_PROCESS_MASTER; + swoole_set_process_type(SW_PROCESS_MASTER); reactor->ptr = this; reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); @@ -438,22 +452,21 @@ int Server::start_master_thread() { reactor->add(pipe_command->get_socket(true), SW_EVENT_READ); } - if ((master_timer = swoole_timer_add(1000, true, Server::timer_callback, this)) == nullptr) { + if ((master_timer = swoole_timer_add(1000L, true, Server::timer_callback, this)) == nullptr) { swoole_event_free(); return SW_ERR; } -#ifdef HAVE_PTHREAD_BARRIER - if (!single_thread) { - pthread_barrier_wait(&reactor_thread_barrier); + if (!single_thread && !is_thread_mode()) { + reactor_thread_barrier.wait(); } - pthread_barrier_wait(&gs->manager_barrier); -#else - SW_START_SLEEP; -#endif + if (is_process_mode()) { + gs->manager_barrier.wait(); + } + gs->master_pid = getpid(); - if (hooks[Server::HOOK_MASTER_START]) { - call_hook(Server::HOOK_MASTER_START, this); + if (isset_hook(HOOK_MASTER_START)) { + call_hook(HOOK_MASTER_START, this); } if (onStart) { @@ -472,6 +485,7 @@ void Server::store_listen_socket() { connection_list[sockfd].socket_type = ls->type; connection_list[sockfd].object = ls; connection_list[sockfd].info.assign(ls->type, ls->host, ls->port); + ls->socket->object = ls; if (sockfd >= 0) { set_minfd(sockfd); set_maxfd(sockfd); @@ -514,6 +528,24 @@ int Server::create_task_workers() { } } + /* + * For Server::task_sync(), create notify pipe and result shared memory. + */ + task_results = (EventData *) sw_shm_calloc(worker_num, sizeof(EventData)); + if (!task_results) { + swoole_warning("malloc[task_result] failed"); + return SW_ERR; + } + SW_LOOP_N(worker_num) { + auto _pipe = new Pipe(true); + if (!_pipe->ready()) { + sw_shm_free(task_results); + delete _pipe; + return SW_ERR; + } + task_notify_pipes.emplace_back(_pipe); + } + init_task_workers(); return SW_OK; @@ -523,7 +555,6 @@ int Server::create_task_workers() { * @description: * only the memory of the Worker structure is allocated, no process is fork. * called when the manager process start. - * @param Server * @return: SW_OK|SW_ERR */ int Server::create_user_workers() { @@ -532,6 +563,14 @@ int Server::create_user_workers() { swoole_sys_warning("gmalloc[server->user_workers] failed"); return SW_ERR; } + + int i = 0; + for (auto worker : user_worker_list) { + memcpy(&user_workers[i], worker, sizeof(user_workers[i])); + create_worker(worker); + i++; + } + return SW_OK; } @@ -540,6 +579,9 @@ int Server::create_user_workers() { */ void Server::create_worker(Worker *worker) { worker->lock = new Mutex(Mutex::PROCESS_SHARED); + if (worker->pipe_object) { + store_pipe_fd(worker->pipe_object); + } } void Server::destroy_worker(Worker *worker) { @@ -550,58 +592,18 @@ void Server::destroy_worker(Worker *worker) { /** * [Worker] */ -void Server::init_worker(Worker *worker) { -#ifdef HAVE_CPU_AFFINITY - if (open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - if (cpu_affinity_available_num) { - CPU_SET(cpu_affinity_available[SwooleG.process_id % cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(SwooleG.process_id % SW_CPU_NUM, &cpu_set); - } - if (swoole_set_cpu_affinity(&cpu_set) < 0) { - swoole_sys_warning("swoole_set_cpu_affinity() failed"); - } - } -#endif - // signal init - worker_signal_init(); - - if (max_request < 1) { - SwooleWG.run_always = true; - } else { - SwooleWG.max_request = max_request; - if (max_request_grace > 0) { - SwooleWG.max_request += swoole_system_random(1, max_request_grace); - } - } - - worker->start_time = ::time(nullptr); - worker->request_count = 0; -} - -void Server::call_worker_start_callback(Worker *worker) { - void *hook_args[2]; - hook_args[0] = this; - hook_args[1] = (void *) (uintptr_t) worker->id; - - if (SwooleG.hooks[SW_GLOBAL_HOOK_BEFORE_WORKER_START]) { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args); - } - if (hooks[Server::HOOK_WORKER_START]) { - call_hook(Server::HOOK_WORKER_START, hook_args); - } - if (onWorkerStart) { - onWorkerStart(this, worker->id); - } +void Server::init_event_worker(Worker *worker) { + worker->init(); + worker->set_max_request(max_request, max_request_grace); } int Server::start() { + swoole_clear_last_error(); + swoole_clear_last_error_msg(); if (start_check() < 0) { return SW_ERR; } - if (SwooleG.hooks[SW_GLOBAL_HOOK_BEFORE_SERVER_START]) { + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_START)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_START, this); } // cannot start 2 servers at the same time, please use process->exec. @@ -634,8 +636,6 @@ int Server::start() { } } - // master pid - gs->master_pid = getpid(); gs->start_time = ::time(nullptr); /** @@ -652,26 +652,6 @@ int Server::start() { gs->event_workers.workers[i].type = SW_PROCESS_WORKER; } - /* - * For swoole_server->taskwait, create notify pipe and result shared memory. - */ - if (task_worker_num > 0 && worker_num > 0) { - task_result = (EventData *) sw_shm_calloc(worker_num, sizeof(EventData)); - if (!task_result) { - swoole_warning("malloc[task_result] failed"); - return SW_ERR; - } - SW_LOOP_N(worker_num) { - auto _pipe = new Pipe(true); - if (!_pipe->ready()) { - sw_shm_free(task_result); - delete _pipe; - return SW_ERR; - } - task_notify_pipes.emplace_back(_pipe); - } - } - if (!user_worker_list.empty()) { uint32_t i = 0; for (auto worker : user_worker_list) { @@ -685,8 +665,6 @@ int Server::start() { if (!factory->start()) { return SW_ERR; } - init_signal_handler(); - // write PID file if (!pid_file.empty()) { size_t n = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, "%d", getpid()); @@ -695,8 +673,13 @@ int Server::start() { int ret; if (is_base_mode()) { ret = start_reactor_processes(); - } else { + } else if (is_process_mode()) { ret = start_reactor_threads(); + } else if (is_thread_mode()) { + ret = start_worker_threads(); + } else { + abort(); + return SW_ERR; } // failed to start if (ret < 0) { @@ -714,39 +697,28 @@ int Server::start() { * initializing server config, set default */ Server::Server(enum Mode _mode) { - swoole_init(); - reactor_num = SW_CPU_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_CPU_NUM; worker_num = SW_CPU_NUM; max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); mode_ = _mode; // http server -#ifdef SW_HAVE_COMPRESSION http_compression = 1; http_compression_level = SW_Z_BEST_SPEED; compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; -#endif -#ifdef __linux__ - timezone_ = timezone; -#else - struct timezone tz; - struct timeval tv; - gettimeofday(&tv, &tz); - timezone_ = tz.tz_minuteswest * 60; -#endif + timezone_ = get_timezone(); - /** - * alloc shared memory - */ gs = (ServerGS *) sw_shm_malloc(sizeof(ServerGS)); if (gs == nullptr) { swoole_error("[Master] Fatal Error: failed to allocate memory for Server->gs"); } - gs->pipe_packet_msg_id = 1; - message_bus.set_id_generator([this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }); + gs->max_concurrency = UINT_MAX; + + msg_id_generator = [this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }; + message_bus.set_id_generator(msg_id_generator); + worker_thread_start = [](const WorkerFn &fn) { fn(); }; g_server_instance = this; } @@ -788,7 +760,11 @@ int Server::create() { } if (enable_static_handler and locations == nullptr) { - locations = new std::unordered_set; + locations = std::make_shared>(); + } + + if (http_compression_types && http_compression_types->size() > 0) { + http_compression = 1; } // Max Connections @@ -803,9 +779,8 @@ int Server::create() { } // Reactor Thread Num if (reactor_num > SW_CPU_NUM * SW_MAX_THREAD_NCPU) { - swoole_warning("serv->reactor_num == %d, Too many threads, reset to max value %d", - reactor_num, - SW_CPU_NUM * SW_MAX_THREAD_NCPU); + swoole_warning( + "reactor_num == %d, Too many threads, reset to max value %d", reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU); reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU; } else if (reactor_num == 0) { reactor_num = SW_CPU_NUM; @@ -825,7 +800,7 @@ int Server::create() { // TaskWorker Process Num if (task_worker_num > 0) { if (task_worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) { - swoole_warning("serv->task_worker_num == %d, Too many processes, reset to max value %d", + swoole_warning("task_worker_num == %d, Too many processes, reset to max value %d", task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; @@ -837,28 +812,27 @@ int Server::create() { return SW_ERR; } - int retval; if (is_base_mode()) { - factory = new BaseFactory(this); - retval = create_reactor_processes(); + factory = create_base_factory(); + } else if (is_thread_mode()) { + factory = create_thread_factory(); } else { - factory = new ProcessFactory(this); - retval = create_reactor_threads(); + factory = create_process_factory(); + } + if (!factory) { + return SW_ERR; } -#ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { - pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); - pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); - pthread_barrier_init(&gs->manager_barrier, &gs->manager_barrier_attr, 2); + reactor_thread_barrier.init(false, reactor_num + 1); + gs->manager_barrier.init(true, 2); } -#endif if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); } - return retval; + return SW_OK; } void Server::clear_timer() { @@ -876,42 +850,138 @@ void Server::clear_timer() { } } -void Server::shutdown() { - swoole_trace_log(SW_TRACE_SERVER, "shutdown service"); - if (getpid() != gs->master_pid) { - kill(gs->master_pid, SIGTERM); - return; +bool Server::shutdown() { + if (sw_unlikely(!is_started())) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + + pid_t pid; + if (is_base_mode()) { + pid = get_manager_pid() == 0 ? get_master_pid() : get_manager_pid(); + } else if (is_thread_mode()) { + if (is_master_thread()) { + stop_master_thread(); + } else { + running = false; + } + return true; + } else { + pid = get_master_pid(); + } + + if (swoole_kill(pid, SIGTERM) < 0) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "failed to shutdown, kill(%d, SIGTERM) failed", pid); + return false; + } + + return true; +} + +bool Server::signal_handler_reload(bool reload_all_workers) { + reload(reload_all_workers); + sw_logger()->reopen(); + return true; +} + +bool Server::signal_handler_read_message() { + gs->event_workers.read_message = true; + return true; +} + +#ifdef SIGRTMIN +bool Server::signal_handler_reopen_logger() { + uint32_t i; + Worker *worker; + for (i = 0; i < worker_num + task_worker_num + get_user_worker_num(); i++) { + worker = get_worker(i); + swoole_kill(worker->pid, SIGRTMIN); } if (is_process_mode()) { - if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); + swoole_kill(gs->manager_pid, SIGRTMIN); + } + sw_logger()->reopen(); + return true; +} +#endif + +void Server::stop_master_thread() { + Reactor *reactor = SwooleTG.reactor; + reactor->set_wait_exit(true); + for (auto port : ports) { + if (port->is_dgram() && !is_base_mode()) { + continue; } - if (onBeforeShutdown) { - onBeforeShutdown(this); + if (!port->socket->removed) { + reactor->del(port->socket); } } - running = false; - // stop all thread - if (SwooleTG.reactor) { - Reactor *reactor = SwooleTG.reactor; - reactor->set_wait_exit(true); - for (auto port : ports) { - if (port->is_dgram() and is_process_mode()) { - continue; + if (pipe_command) { + reactor->del(pipe_command->get_socket(true)); + } + clear_timer(); + if (max_wait_time > 0) { + time_t shutdown_time = std::time(nullptr); + auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { + time_t now = std::time(nullptr); + if (now - shutdown_time > max_wait_time) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + "graceful shutdown failed, forced termination"); + reactor->running = false; } - reactor->del(port->socket); - } - if (pipe_command) { - reactor->del(pipe_command->get_socket(true)); - } - clear_timer(); + return true; + }; + reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); + } + if (is_thread_mode()) { + stop_worker_threads(); + } + if (is_process_mode() && single_thread) { + get_thread(0)->shutdown(reactor); } +} +bool Server::signal_handler_shutdown() { + swoole_trace_log(SW_TRACE_SERVER, "shutdown begin"); if (is_base_mode()) { - gs->event_workers.running = 0; + if (gs->manager_pid > 0) { + running = false; + } else { + // single process worker, exit directly + gs->event_workers.running = 0; + stop_async_worker(sw_worker()); + } + return true; } + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); + } + if (onBeforeShutdown) { + onBeforeShutdown(this); + } + running = false; + stop_master_thread(); + swoole_trace_log(SW_TRACE_SERVER, "shutdown end"); + return true; +} - swoole_info("Server is shutdown now"); +bool Server::signal_handler_child_exit() { + if (!running) { + return false; + } + if (is_base_mode()) { + return false; + } + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid > 0 && pid == gs->manager_pid) { + swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", + WEXITSTATUS(status), + swoole_signal_to_str(WTERMSIG(status))); + } + return true; } void Server::destroy() { @@ -919,17 +989,20 @@ void Server::destroy() { if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN)) { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN, this); } - /** - * shutdown workers - */ + factory->shutdown(); + + SW_LOOP_N(worker_num) { + Worker *worker = &workers[i]; + destroy_worker(worker); + } + if (is_base_mode()) { swoole_trace_log(SW_TRACE_SERVER, "terminate task workers"); if (task_worker_num > 0) { - gs->task_workers.shutdown(); gs->task_workers.destroy(); } - } else { + } else if (is_process_mode()) { swoole_trace_log(SW_TRACE_SERVER, "terminate reactor threads"); /** * Wait until all the end of the thread @@ -937,6 +1010,8 @@ void Server::destroy() { join_reactor_thread(); } + release_pipe_buffers(); + for (auto port : ports) { port->close(); } @@ -961,31 +1036,25 @@ void Server::destroy() { if (onShutdown) { onShutdown(this); } - if (is_base_mode()) { - destroy_reactor_processes(); - } else { - destroy_reactor_threads(); - } - if (locations) { - delete locations; - } - if (http_index_files) { - delete http_index_files; - } - for (auto i = 0; i < SW_MAX_HOOK_TYPE; i++) { + SW_LOOP_N(SW_MAX_HOOK_TYPE) { if (hooks[i]) { std::list *l = reinterpret_cast *>(hooks[i]); hooks[i] = nullptr; delete l; } } -#ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { - pthread_barrier_destroy(&reactor_thread_barrier); - pthread_barrier_destroy(&gs->manager_barrier); - pthread_barrierattr_destroy(&gs->manager_barrier_attr); + reactor_thread_barrier.destroy(); + gs->manager_barrier.destroy(); } -#endif + if (is_base_mode()) { + destroy_base_factory(); + } else if (is_thread_mode()) { + destroy_thread_factory(); + } else { + destroy_process_factory(); + } + sw_shm_free(session_list); sw_shm_free(port_gs_list); sw_shm_free(workers); @@ -1033,7 +1102,7 @@ bool Server::command(WorkerId process_id, if (is_process_mode() && !is_master()) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in master process"); return false; - } else if (is_base_mode() && SwooleWG.worker->id != 0) { + } else if (is_base_mode() && sw_worker()->id != 0) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in worker process 0"); return false; } @@ -1048,56 +1117,58 @@ bool Server::command(WorkerId process_id, } int command_id = iter->second.id; - int64_t requset_id = command_current_request_id++; + int64_t request_id = command_current_request_id++; Socket *pipe_sock; SendData task{}; - task.info.fd = requset_id; + task.info.fd = request_id; task.info.reactor_id = process_id; task.info.server_fd = command_id; task.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; task.info.len = msg.length(); task.data = msg.c_str(); + command_callbacks[request_id] = fn; + if (!(process_type & iter->second.accepted_process_types)) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [process_type]"); + _fail: + command_callbacks.erase(request_id); return false; } if (process_type == Command::REACTOR_THREAD) { if (!is_process_mode()) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [server_mode]"); - return false; + goto _fail; } if (process_id >= reactor_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid thread_id[%d]", process_id); - return false; + goto _fail; } pipe_sock = get_worker(process_id)->pipe_worker; } else if (process_type == Command::EVENT_WORKER) { if (process_id >= worker_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid worker_id[%d]", process_id); - return false; + goto _fail; } pipe_sock = get_worker(process_id)->pipe_master; } else if (process_type == Command::TASK_WORKER) { if (process_id >= task_worker_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid task_worker_id[%d]", process_id); - return false; + goto _fail; } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); if (!task_pack(&buf, msg.c_str(), msg.length())) { - return false; + goto _fail; } buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; - buf.info.fd = requset_id; + buf.info.fd = request_id; buf.info.server_fd = command_id; int _dst_worker_id = process_id; - if (gs->task_workers.dispatch(&buf, &_dst_worker_id) <= 0) { - return false; + if (!this->task(&buf, &_dst_worker_id)) { + goto _fail; } - command_callbacks[requset_id] = fn; return true; } else if (process_type == Command::MANAGER) { EventData buf; @@ -1107,18 +1178,17 @@ bool Server::command(WorkerId process_id, "message is too large, maximum length is %lu, the given length is %lu", sizeof(buf.data), msg.length()); - return false; + goto _fail; } memset(&buf.info, 0, sizeof(buf.info)); buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; - buf.info.fd = requset_id; + buf.info.fd = request_id; buf.info.server_fd = command_id; buf.info.len = msg.length(); memcpy(buf.data, msg.c_str(), msg.length()); if (gs->event_workers.push_message(&buf) < 0) { - return false; + goto _fail; } - command_callbacks[requset_id] = fn; return true; } else if (process_type == Command::MASTER) { auto result = call_command_handler_in_master(command_id, msg); @@ -1126,12 +1196,11 @@ bool Server::command(WorkerId process_id, return true; } else { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [process_type]"); - return false; + goto _fail; } if (!message_bus.write(pipe_sock, &task)) { - return false; + goto _fail; } - command_callbacks[requset_id] = fn; return true; } @@ -1167,8 +1236,8 @@ bool Server::send(SessionId session_id, const void *data, uint32_t length) { sw_atomic_fetch_add(&port->gs->response_count, 1); sw_atomic_fetch_add(&port->gs->total_send_bytes, length); } - if (SwooleWG.worker) { - SwooleWG.worker->response_count++; + if (sw_worker()) { + sw_worker()->response_count++; } return true; } @@ -1232,23 +1301,12 @@ int Server::schedule_worker(int fd, SendData *data) { return conn->worker_id; } else if (dispatch_mode == DISPATCH_CO_REQ_LB) { return get_lowest_load_worker_id(); + } else if (dispatch_mode == DISPATCH_CONCURRENT_LB) { + return get_lowest_concurrent_worker_id(); } // deliver tasks to idle worker processes else { - uint32_t i; - bool found = false; - for (i = 0; i < worker_num + 1; i++) { - key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; - if (workers[key].status == SW_WORKER_IDLE) { - found = true; - break; - } - } - if (sw_unlikely(!found)) { - scheduler_warning = true; - } - swoole_trace_log(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); - return key; + return get_idle_worker_id(); } return key % worker_num; @@ -1271,13 +1329,13 @@ int Server::send_to_connection(SendData *_send) { } if (!conn) { if (_send->info.type == SW_SERVER_EVENT_SEND_DATA) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "send %d byte failed, session#%ld does not exist", _send_length, session_id); } else { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "send event[%d] failed, session#%ld does not exist", _send->info.type, @@ -1295,7 +1353,7 @@ int Server::send_to_connection(SendData *_send) { assert(fd % reactor_num == SwooleTG.id); } - if (is_base_mode() && conn->overflow) { + if (!is_process_mode() && conn->overflow) { if (send_yield) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { @@ -1380,11 +1438,9 @@ int Server::send_to_connection(SendData *_send) { } } - BufferChunk *chunk; // close connection if (_send->info.type == SW_SERVER_EVENT_CLOSE) { - chunk = _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); - chunk->value.data.val1 = _send->info.type; + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); conn->close_queued = 1; } // sendfile to client @@ -1398,8 +1454,7 @@ int Server::send_to_connection(SendData *_send) { else { // connection is closed if (conn->peer_closed) { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "Server::send(): socket#%d is closed by client", fd); + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "socket#%d is closed by client", fd); return false; } // connection output buffer overflow @@ -1407,10 +1462,8 @@ int Server::send_to_connection(SendData *_send) { if (send_yield) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_OUTPUT_BUFFER_OVERFLOW, - "Server::send(): connection#%d output buffer overflow", - fd); + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "connection#%d output buffer overflow", fd); } conn->overflow = 1; if (onBufferEmpty && onBufferFull == nullptr) { @@ -1432,7 +1485,7 @@ int Server::send_to_connection(SendData *_send) { auto timeout_callback = get_timeout_callback(port, reactor, conn); _socket->send_timeout_ = port->max_idle_time; _socket->last_sent_time = time(true); - _socket->send_timer = swoole_timer_add(port->max_idle_time * 1000, true, timeout_callback); + _socket->send_timer = swoole_timer_add((long) (port->max_idle_time * 1000), true, timeout_callback); } if (!_socket->isset_writable_event()) { @@ -1479,7 +1532,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o "sendfile name[%.8s...] length %u is exceed the max name len %u", file, l_file, - (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); return false; } // string must be zero termination (for `state` system call) @@ -1491,6 +1544,13 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "stat(%s) failed", req->filename); return false; } + if (!S_ISREG(file_stat.st_mode)) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, + "the path[%s] given is not a regular file", + req->filename); + return false; + } if (file_stat.st_size <= offset) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "file[offset=%ld] is empty", (long) offset); return false; @@ -1514,9 +1574,9 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { Connection *conn = get_connection_verify(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, - SW_ERROR_SESSION_CLOSED, - "send %d byte failed, because session#%ld is closed", + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "send %d byte failed, because session#%ld is not exists", length, session_id); return false; @@ -1525,6 +1585,7 @@ bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { } void Server::call_hook(HookType type, void *arg) { + assert(type <= HOOK_END); swoole::hook_call(hooks, type, arg); } @@ -1535,24 +1596,32 @@ bool Server::close(SessionId session_id, bool reset) { return factory->end(session_id, reset ? (CLOSE_ACTIVELY | CLOSE_RESET) : CLOSE_ACTIVELY); } +bool Server::send_pipe_message(WorkerId worker_id, EventData *msg) { + msg->info.type = SW_SERVER_EVENT_PIPE_MESSAGE; + + return send_to_worker_from_worker(get_worker(worker_id), msg, msg->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) > 0; +} + void Server::init_signal_handler() { swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGHUP, nullptr); if (is_process_mode()) { - swoole_signal_set(SIGCHLD, Server_signal_handler); + swoole_signal_set(SIGCHLD, master_signal_handler); } else { - swoole_signal_set(SIGIO, Server_signal_handler); + swoole_signal_set(SIGIO, master_signal_handler); } - swoole_signal_set(SIGUSR1, Server_signal_handler); - swoole_signal_set(SIGUSR2, Server_signal_handler); - swoole_signal_set(SIGTERM, Server_signal_handler); + swoole_signal_set(SIGUSR1, master_signal_handler); + swoole_signal_set(SIGUSR2, master_signal_handler); + swoole_signal_set(SIGTERM, master_signal_handler); #ifdef SIGRTMIN - swoole_signal_set(SIGRTMIN, Server_signal_handler); + swoole_signal_set(SIGRTMIN, master_signal_handler); #endif // for test - swoole_signal_set(SIGVTALRM, Server_signal_handler); + swoole_signal_set(SIGVTALRM, master_signal_handler); - set_minfd(SwooleG.signal_fd); + if (SwooleG.signal_fd > 0) { + set_minfd(SwooleG.signal_fd); + } } void Server::timer_callback(Timer *timer, TimerNode *tnode) { @@ -1573,10 +1642,16 @@ void Server::timer_callback(Timer *timer, TimerNode *tnode) { if (serv->hooks[Server::HOOK_MASTER_TIMER]) { serv->call_hook(Server::HOOK_MASTER_TIMER, serv); } + + if (!serv->is_running()) { + sw_reactor()->running = false; + serv->stop_master_thread(); + } } int Server::add_worker(Worker *worker) { user_worker_list.push_back(worker); + worker->id = user_worker_list.size() - 1; return worker->id; } @@ -1591,7 +1666,7 @@ bool Server::add_command(const std::string &name, int accepted_process_types, co if (commands.find(name) != commands.end()) { return false; } - if (is_process_mode() && pipe_command == nullptr) { + if (!is_base_mode() && pipe_command == nullptr) { auto _pipe = new UnixSocket(false, SOCK_DGRAM); if (!_pipe->ready()) { delete _pipe; @@ -1725,7 +1800,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (SocketType)(type & (~SW_SOCK_SSL)); + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); @@ -1740,7 +1815,6 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_SUPPORT_DTLS ls->ssl_context->protocols = SW_SSL_DTLS; ls->dtls_sessions = new std::unordered_map; - #else swoole_warning("DTLS support require openssl-1.1 or later"); return nullptr; @@ -1749,94 +1823,46 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { } #endif - ls->socket = make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket == nullptr) { + if (ls->create_socket(this) < 0) { + swoole_set_last_error(errno); return nullptr; } -#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) - if (ls->is_dtls()) { - ls->socket->set_reuse_port(); - } -#endif - if (ls->socket->bind(ls->host, &ls->port) < 0) { - ls->socket->free(); - return nullptr; - } - ls->socket->info.assign(ls->type, ls->host, ls->port); check_port_type(ls); ptr.release(); ports.push_back(ls); return ls; } -static void Server_signal_handler(int sig) { - swoole_trace_log(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", sig, swoole_signal_to_str(sig), getpid()); +void Server::master_signal_handler(int signo) { + swoole_trace_log(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", signo, swoole_signal_to_str(signo), getpid()); Server *serv = sw_server(); - if (!SwooleG.running or !serv) { + if (!SwooleG.running || !serv || !serv->is_running()) { return; } - int status; - pid_t pid; - switch (sig) { + switch (signo) { case SIGTERM: - serv->shutdown(); + serv->signal_handler_shutdown(); break; case SIGCHLD: - if (!serv->running) { - break; - } - if (sw_server()->is_base_mode()) { - break; - } - pid = waitpid(-1, &status, WNOHANG); - if (pid > 0 && pid == serv->gs->manager_pid) { - swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", - WEXITSTATUS(status), - swoole_signal_to_str(WTERMSIG(status))); - } + serv->signal_handler_child_exit(); break; - /** - * for test - */ case SIGVTALRM: swoole_warning("SIGVTALRM coming"); break; - /** - * proxy the restart signal - */ case SIGUSR1: case SIGUSR2: - if (serv->is_base_mode()) { - if (!serv->gs->event_workers.reload()) { - break; - } - serv->gs->event_workers.reload_init = false; - } else { - swoole_kill(serv->gs->manager_pid, sig); - } - sw_logger()->reopen(); + serv->signal_handler_reload(signo == SIGUSR1); break; case SIGIO: - serv->gs->event_workers.read_message = true; + serv->signal_handler_read_message(); break; default: - #ifdef SIGRTMIN - if (sig == SIGRTMIN) { - uint32_t i; - Worker *worker; - for (i = 0; i < serv->worker_num + serv->task_worker_num + serv->get_user_worker_num(); i++) { - worker = serv->get_worker(i); - swoole_kill(worker->pid, SIGRTMIN); - } - if (serv->is_process_mode()) { - swoole_kill(serv->gs->manager_pid, SIGRTMIN); - } - sw_logger()->reopen(); + if (signo == SIGRTMIN) { + serv->signal_handler_reopen_logger(); } #endif break; @@ -1862,6 +1888,34 @@ void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) } } +// see https://github.com/swoole/swoole-src/issues/5407 +// see https://github.com/swoole/swoole-src/issues/5432 +void Server::reset_worker_counter(Worker *worker) { + auto value = worker->concurrency; + if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) { + sw_atomic_sub_fetch(&gs->concurrency, value); + if ((int) gs->concurrency < 0) { + gs->concurrency = 0; + } + } + worker->request_count = 0; + worker->response_count = 0; + worker->dispatch_count = 0; +} + +void Server::abort_worker(Worker *worker) { + reset_worker_counter(worker); + + if (is_base_mode()) { + SW_LOOP_N(SW_SESSION_LIST_SIZE) { + Session *session = get_session(i); + if (session->reactor_id == worker->id) { + session->fd = 0; + } + } + } +} + /** * new connection */ @@ -1869,7 +1923,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f int fd = _socket->fd; Connection *connection = &(connection_list[fd]); - ReactorId reactor_id = is_base_mode() ? SwooleG.process_id : fd % reactor_num; + ReactorId reactor_id = is_base_mode() ? swoole_get_process_id() : fd % reactor_num; *connection = {}; sw_spinlock(&gs->spinlock); @@ -1901,7 +1955,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f // TCP Nodelay if (ls->open_tcp_nodelay && (ls->type == SW_SOCK_TCP || ls->type == SW_SOCK_TCP6)) { - if (ls->socket->set_tcp_nodelay() != 0) { + if (_socket->set_tcp_nodelay() != 0) { swoole_sys_warning("setsockopt(TCP_NODELAY) failed"); } _socket->enable_tcp_nodelay = true; @@ -1948,14 +2002,19 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f gs->accept_count++; ls->gs->accept_count++; - sw_atomic_fetch_add(&gs->connection_num, 1); - sw_atomic_fetch_add(&ls->gs->connection_num, 1); + if (is_base_mode()) { + sw_atomic_fetch_add(&gs->connection_nums[reactor_id], 1); + sw_atomic_fetch_add(&ls->gs->connection_nums[reactor_id], 1); + } else { + sw_atomic_fetch_add(&gs->connection_num, 1); + sw_atomic_fetch_add(&ls->gs->connection_num, 1); + } return connection; } void Server::init_ipc_max_size() { -#ifdef HAVE_KQUEUE +#ifndef __linux__ ipc_max_size = SW_IPC_MAX_SIZE; #else int bufsize; @@ -1965,10 +2024,24 @@ void Server::init_ipc_max_size() { if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { bufsize = SW_IPC_MAX_SIZE; } - ipc_max_size = bufsize - SW_DGRAM_HEADER_SIZE; + ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE) - SW_DGRAM_HEADER_SIZE; #endif } +void Server::init_pipe_sockets(MessageBus *mb) { + assert(is_started()); + size_t n = get_core_worker_num(); + + SW_LOOP_N(n) { + Worker *worker = get_worker(i); + if (i >= worker_num && task_ipc_mode != TASK_IPC_UNIXSOCK) { + continue; + } + mb->init_pipe_socket(worker->pipe_master); + mb->init_pipe_socket(worker->pipe_worker); + } +} + /** * allocate memory for Server::pipe_buffers */ @@ -1977,13 +2050,17 @@ int Server::create_pipe_buffers() { return message_bus.alloc_buffer() ? SW_OK : SW_ERR; } +void Server::release_pipe_buffers() { + message_bus.free_buffer(); +} + int Server::get_idle_worker_num() { uint32_t i; uint32_t idle_worker_num = 0; for (i = 0; i < worker_num; i++) { Worker *worker = get_worker(i); - if (worker->status == SW_WORKER_IDLE) { + if (worker->is_idle()) { idle_worker_num++; } } @@ -1996,7 +2073,7 @@ int Server::get_idle_task_worker_num() { for (uint32_t i = worker_num; i < (worker_num + task_worker_num); i++) { Worker *worker = get_worker(i); - if (worker->status == SW_WORKER_IDLE) { + if (worker->is_idle()) { idle_worker_num++; } } @@ -2004,7 +2081,7 @@ int Server::get_idle_task_worker_num() { return idle_worker_num; } -int Server::get_task_count() { +int Server::get_tasking_num() { // TODO Why need to reset ? int tasking_num = gs->tasking_num; if (tasking_num < 0) { diff --git a/src/server/port.cc b/src/server/port.cc index bd5b3d0325d..7bc3699104a 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -53,8 +53,7 @@ bool ListenPort::ssl_add_sni_cert(const std::string &name, SSLContext *ctx) { return true; } -static bool ssl_matches_wildcard_name(const char *subjectname, const char *certname) /* {{{ */ -{ +static bool ssl_matches_wildcard_name(const char *subjectname, const char *certname) { const char *wildcard = NULL; ptrdiff_t prefix_len; size_t suffix_len, subject_len; @@ -143,7 +142,7 @@ bool ListenPort::ssl_create_context(SSLContext *context) { context->http_v2 = 1; } if (!context->create()) { - swoole_warning("swSSL_get_context() error"); + swoole_warning("failed to create ssl content"); return false; } return true; @@ -232,7 +231,6 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.onPackage = Server::dispatch_task; ls->onRead = Port_onRead_check_length; } else if (ls->open_http_protocol) { -#ifdef SW_USE_HTTP2 if (ls->open_http2_protocol && ls->open_websocket_protocol) { ls->protocol.get_package_length = http_server::get_package_length; ls->protocol.get_package_length_size = http_server::get_package_length_size; @@ -241,10 +239,8 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; ls->protocol.get_package_length = http2::get_frame_length; ls->protocol.onPackage = Server::dispatch_task; - } else -#endif - if (ls->open_websocket_protocol) { - ls->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); + } else if (ls->open_websocket_protocol) { + ls->protocol.package_length_size = SW_WEBSOCKET_MESSAGE_HEADER_SIZE; ls->protocol.get_package_length = websocket::get_package_length; ls->protocol.onPackage = websocket::dispatch_frame; } @@ -299,9 +295,7 @@ void ListenPort::clear_protocol() { open_length_check = 0; open_http_protocol = 0; open_websocket_protocol = 0; -#ifdef SW_USE_HTTP2 open_http2_protocol = 0; -#endif open_mqtt_protocol = 0; open_redis_protocol = 0; } @@ -311,6 +305,7 @@ static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) { Socket *_socket = event->socket; Connection *conn = (Connection *) _socket->object; Server *serv = (Server *) reactor->ptr; + RecvData rdata{}; String *buffer = serv->get_recv_buffer(_socket); if (!buffer) { @@ -319,7 +314,7 @@ static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) { n = _socket->recv(buffer->str, buffer->size, 0); if (n < 0) { - switch (_socket->catch_error(errno)) { + switch (_socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; @@ -335,7 +330,9 @@ static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) { return SW_OK; } else { buffer->offset = buffer->length = n; - return Server::dispatch_task(&port->protocol, _socket, buffer->str, n); + rdata.info.len = n; + rdata.data = buffer->str; + return Server::dispatch_task(&port->protocol, _socket, &rdata); } } @@ -379,6 +376,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { Socket *_socket = event->socket; Connection *conn = (Connection *) _socket->object; Server *serv = (Server *) reactor->ptr; + RecvData dispatch_data{}; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { if (conn->http_upgrade == 0) { @@ -389,11 +387,9 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { return Port_onRead_check_length(reactor, port, event); } -#ifdef SW_USE_HTTP2 if (conn->http2_stream) { return Port_onRead_check_length(reactor, port, event); } -#endif Request *request = nullptr; Protocol *protocol = &port->protocol; @@ -418,7 +414,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { _recv_data: ssize_t n = _socket->recv(buffer->str + buffer->length, buffer->size - buffer->length, 0); if (n < 0) { - switch (_socket->catch_error(errno)) { + switch (_socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; @@ -433,21 +429,15 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (n == 0) { if (0) { _bad_request: -#ifdef SW_HTTP_BAD_REQUEST_PACKET _socket->send(SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0); -#endif } if (0) { _too_large: -#ifdef SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET _socket->send(SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0); -#endif } if (0) { _unavailable: -#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); -#endif } _close_fd: serv->destroy_http_request(conn); @@ -476,15 +466,12 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { CLIENT_INFO_ARGS); goto _bad_request; } else if (request->method == SW_HTTP_PRI) { -#ifdef SW_USE_HTTP2 if (sw_unlikely(!port->open_http2_protocol)) { -#endif swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: can not handle HTTP2 request" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); goto _bad_request; -#ifdef SW_USE_HTTP2 } conn->http2_stream = 1; http2::send_setting_frame(protocol, _socket); @@ -497,7 +484,6 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { serv->destroy_http_request(conn); conn->socket->skip_recv = 1; return Port_onRead_check_length(reactor, port, event); -#endif } // http header is not the end @@ -517,18 +503,50 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { // parse http header and got http body length if (!request->header_parsed) { request->parse_header_info(); + request->max_length_ = protocol->package_max_length; swoole_trace_log(SW_TRACE_SERVER, - "content-length=%u, keep-alive=%u, chunked=%u", + "content-length=%" PRIu64 ", keep-alive=%u, chunked=%u", request->content_length_, request->keep_alive, request->chunked); + if (request->form_data_) { + if (serv->upload_max_filesize > 0 && + request->header_length_ + request->content_length_ > request->max_length_) { + request->init_multipart_parser(serv); + + buffer = request->buffer_; + } else { + delete request->form_data_; + request->form_data_ = nullptr; + } + } + } + + if (request->form_data_) { + if (!request->multipart_header_parsed && memmem(buffer->str, buffer->length, SW_STRL("\r\n\r\n")) == nullptr) { + return SW_OK; + } + if (!request->parse_multipart_data(buffer)) { + goto _bad_request; + } + if (request->too_large) { + goto _too_large; + } + if (request->unavailable) { + goto _unavailable; + } + if (!request->tried_to_dispatch) { + return SW_OK; + } + request->destroy_multipart_parser(); + buffer = request->buffer_; } // content length (equal to 0) or (field not found but not chunked) if (!request->tried_to_dispatch) { // recv nobody_chunked eof if (request->nobody_chunked) { - if (buffer->length < request->header_length_ + (sizeof("0\r\n\r\n") - 1)) { + if (buffer->length < request->header_length_ + (sizeof(SW_HTTP_CHUNK_EOF) - 1)) { goto _recv_data; } request->header_length_ += (sizeof("0\r\n\r\n") - 1); @@ -540,7 +558,11 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { // send static file content directly in the reactor thread if (!serv->enable_static_handler || !serv->select_static_handler(request, conn)) { // dynamic request, dispatch to worker - Server::dispatch_task(protocol, _socket, buffer->str, request->header_length_); + dispatch_data.info.len = request->header_length_; + dispatch_data.data = buffer->str; + if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) { + goto _close_fd; + } } if (!conn->active || _socket->removed) { return SW_OK; @@ -589,13 +611,15 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { } else { request_length = request->header_length_ + request->content_length_; } - swoole_trace_log(SW_TRACE_SERVER, "received chunked eof, real content-length=%u", request->content_length_); + swoole_trace_log( + SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_); } else { request_length = request->header_length_ + request->content_length_; if (request_length > protocol->package_max_length) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Request Entity Too Large: header-length (%u) + content-length (%u) is greater than the " + "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64 + ") is greater than the " "package_max_length(%u)" CLIENT_INFO_FMT, request->header_length_, request->content_length_, @@ -637,7 +661,12 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { } buffer->offset = request_length; - Server::dispatch_task(protocol, _socket, buffer->str, buffer->length); + dispatch_data.data = buffer->str; + dispatch_data.info.len = buffer->length; + + if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) { + goto _close_fd; + } if (conn->active && !_socket->removed) { serv->destroy_http_request(conn); @@ -733,14 +762,11 @@ const char *ListenPort::get_protocols() { } else if (open_length_check) { return "length"; } else if (open_http_protocol) { -#ifdef SW_USE_HTTP2 if (open_http2_protocol && open_websocket_protocol) { return "http|http2|websocket"; } else if (open_http2_protocol) { return "http|http2"; - } else -#endif - if (open_websocket_protocol) { + } else if (open_websocket_protocol) { return "http|websocket"; } else { return "http"; @@ -754,4 +780,68 @@ const char *ListenPort::get_protocols() { } } +size_t ListenPort::get_connection_num() const { + if (gs->connection_nums) { + size_t num = 0; + for (uint32_t i = 0; i < sw_server()->worker_num; i++) { + num += gs->connection_nums[i]; + } + return num; + } else { + return gs->connection_num; + } +} + +int ListenPort::create_socket(Server *server) { + if (socket) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + close_socket(); + } else +#endif + { + return SW_OK; + } + } + + socket = make_socket( + type, is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + if (socket == nullptr) { + swoole_set_last_error(errno); + return SW_ERR; + } + +#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) + if (is_dtls()) { + socket->set_reuse_port(); + } +#endif + +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + if (socket->set_reuse_port() < 0) { + socket->free(); + return SW_ERR; + } + } +#endif + + if (socket->bind(host, &port) < 0) { + swoole_set_last_error(errno); + socket->free(); + return SW_ERR; + } + + socket->info.assign(type, host, port); + return SW_OK; +} + +void ListenPort::close_socket() { + if (::close(socket->fd) < 0) { + swoole_sys_warning("close(%d) failed", socket->fd); + } + delete socket; + socket = nullptr; +} + } // namespace swoole diff --git a/src/server/process.cc b/src/server/process.cc index 9468322052c..3e677260c0b 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -10,97 +10,203 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ -#include - #include "swoole_server.h" namespace swoole { using network::Socket; +Factory *Server::create_process_factory() { + /** + * init reactor thread pool + */ + reactor_threads = new ReactorThread[reactor_num](); + /** + * alloc the memory for connection_list + */ + connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_error("calloc[1] failed"); + return nullptr; + } + reactor_pipe_num = worker_num / reactor_num; + return new ProcessFactory(this); +} + +void Server::destroy_process_factory() { + sw_shm_free(connection_list); + delete[] reactor_threads; + + if (gs->event_workers.message_box) { + gs->event_workers.message_box->destroy(); + } +} + ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} -bool ProcessFactory::shutdown() { - int status; +ProcessFactory::~ProcessFactory() {} - if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) { - swoole_sys_warning("kill(%d) failed", server_->gs->manager_pid); +/** + * kill and wait all user process + */ +void Factory::kill_user_workers() { + if (server_->user_worker_map.empty()) { + return; } - if (swoole_waitpid(server_->gs->manager_pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", server_->gs->manager_pid); + for (auto &kv : server_->user_worker_map) { + swoole_kill(kv.second->pid, SIGTERM); + } + + for (auto &kv : server_->user_worker_map) { + int __stat_loc; + if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", kv.second->pid); + } + } +} + +/** + * [Manager] kill and wait all event worker process + */ +void Factory::kill_event_workers() { + int status; + + if (server_->worker_num == 0) { + return; } SW_LOOP_N(server_->worker_num) { - Worker *worker = &server_->workers[i]; - server_->destroy_worker(worker); + swoole_trace_log(SW_TRACE_SERVER, "kill worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); + swoole_kill(server_->workers[i].pid, SIGTERM); } + SW_LOOP_N(server_->worker_num) { + swoole_trace_log(SW_TRACE_SERVER, "wait worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); + if (swoole_waitpid(server_->workers[i].pid, &status, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", server_->workers[i].pid); + } + } +} - return SW_OK; +/** + * [Manager] kill and wait task worker process + */ +void Factory::kill_task_workers() { + if (server_->task_worker_num == 0) { + return; + } + server_->gs->task_workers.shutdown(); } -ProcessFactory::~ProcessFactory() { - if (server_->stream_socket_file) { - unlink(server_->stream_socket_file); - sw_free(server_->stream_socket_file); - server_->stream_socket->free(); +pid_t Factory::spawn_event_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + + if (pid < 0) { + swoole_sys_warning("failed to fork event worker"); + return SW_ERR; + } else if (pid == 0) { + worker->pid = SwooleG.pid; + SwooleWG.worker = worker; + } else { + worker->pid = pid; + return pid; + } + + if (server_->is_base_mode()) { + server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); + } else { + server_->start_event_worker(worker); } + + exit(0); + return 0; } -bool ProcessFactory::start() { - if (server_->dispatch_mode == Server::DISPATCH_STREAM) { - server_->stream_socket_file = swoole_string_format(64, "/tmp/swoole.%d.sock", server_->gs->master_pid); - if (server_->stream_socket_file == nullptr) { - return false; - } - Socket *sock = swoole::make_server_socket(SW_SOCK_UNIX_STREAM, server_->stream_socket_file); - if (sock == nullptr) { - return false; - } - sock->set_fd_option(1, 1); - server_->stream_socket = sock; +pid_t Factory::spawn_user_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + if (worker->pid) { + server_->user_worker_map.erase(worker->pid); + } + if (pid < 0) { + swoole_sys_warning("failed to spawn the user worker"); + return SW_ERR; } + // child + else if (pid == 0) { + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_process_id(worker->id); + worker->pid = SwooleG.pid; + SwooleWG.worker = worker; + server_->onUserWorkerStart(server_, worker); + exit(0); + } + // parent + else { + /** + * worker: local memory + * user_workers: shared memory + */ + server_->get_worker(worker->id)->pid = worker->pid = pid; + server_->user_worker_map.emplace(std::make_pair(pid, worker)); + return pid; + } +} - SW_LOOP_N(server_->worker_num) { - server_->create_worker(server_->get_worker(i)); +pid_t Factory::spawn_task_worker(Worker *worker) { + return server_->gs->task_workers.spawn(worker); +} + +void Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { + if (exit_status.get_status() != 0) { + worker->report_error(exit_status); + server_->call_worker_error_callback(worker, exit_status); } +} - SW_LOOP_N(server_->worker_num) { - int kernel_buffer_size = SW_UNIXSOCK_MAX_BUF_SIZE; +bool ProcessFactory::shutdown() { + int status; + + if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d) failed", server_->gs->manager_pid); + } + + if (swoole_waitpid(server_->gs->manager_pid, &status, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", server_->gs->manager_pid); + } + + return SW_OK; +} + +bool Server::create_worker_pipes() { + SW_LOOP_N(worker_num) { auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { delete _sock; return false; } - pipes.emplace_back(_sock); - server_->workers[i].pipe_master = _sock->get_socket(true); - server_->workers[i].pipe_worker = _sock->get_socket(false); - - server_->workers[i].pipe_master->set_send_buffer_size(kernel_buffer_size); - server_->workers[i].pipe_worker->set_send_buffer_size(kernel_buffer_size); - - server_->workers[i].pipe_object = _sock; - server_->store_pipe_fd(server_->workers[i].pipe_object); + worker_pipes.emplace_back(_sock); + workers[i].pipe_master = _sock->get_socket(true); + workers[i].pipe_worker = _sock->get_socket(false); + workers[i].pipe_object = _sock; } - server_->init_ipc_max_size(); - if (server_->create_pipe_buffers() < 0) { + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { return false; } + return true; +} - /** - * The manager process must be started first, otherwise it will have a thread fork - */ - if (server_->start_manager_process() < 0) { - swoole_warning("failed to start"); +bool ProcessFactory::start() { + if (!server_->create_worker_pipes()) { return false; } - return true; + return server_->start_manager_process() == SW_OK; } /** @@ -159,15 +265,25 @@ bool ProcessFactory::dispatch(SendData *task) { SendData _task; memcpy(&_task, task, sizeof(SendData)); + network::Socket *sock; + MessageBus *mb; + + if (server_->is_reactor_thread() || server_->single_thread) { + mb = &server_->get_thread(swoole_get_thread_id())->message_bus; + sock = mb->get_pipe_socket(worker->pipe_master); + } else { + mb = &server_->message_bus; + sock = worker->pipe_master; + } - return server_->message_bus.write(server_->get_worker_pipe_socket(worker), &_task); + return mb->write(sock, &_task); } static bool inline process_is_supported_send_yield(Server *serv, Connection *conn) { if (!serv->is_hash_dispatch_mode()) { return false; } else { - return serv->schedule_worker(conn->fd, nullptr) == (int) SwooleG.process_id; + return serv->schedule_worker(conn->fd, nullptr) == (int) swoole_get_process_id(); } } @@ -196,10 +312,12 @@ bool ProcessFactory::finish(SendData *resp) { conn = server_->get_connection_verify_no_ssl(session_id); } if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + if (resp->info.type != SW_SERVER_EVENT_CLOSE) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + } return false; } else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "send %d bytes failed, because session#%ld is closed", resp->info.len, @@ -218,29 +336,11 @@ bool ProcessFactory::finish(SendData *resp) { return false; } - /** - * stream - */ - if (server_->last_stream_socket) { - uint32_t _len = resp->info.len; - uint32_t _header = htonl(_len + sizeof(resp->info)); - if (swoole_event_write(server_->last_stream_socket, (char *) &_header, sizeof(_header)) < 0) { - return false; - } - if (swoole_event_write(server_->last_stream_socket, &resp->info, sizeof(resp->info)) < 0) { - return false; - } - if (_len > 0 && swoole_event_write(server_->last_stream_socket, resp->data, _len) < 0) { - return false; - } - return true; - } - SendData task; memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; task.info.reactor_id = conn->reactor_id; - task.info.server_fd = SwooleG.process_id; + task.info.server_fd = swoole_get_process_id(); swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); @@ -257,7 +357,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { Connection *conn = server_->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); return false; } // Reset send buffer, Immediately close the connection. @@ -275,29 +375,21 @@ bool ProcessFactory::end(SessionId session_id, int flags) { DataHead ev = {}; /** - * Only active shutdown needs to determine whether it is in the process of connection binding + * Only close actively needs to determine whether it is in the process of connection binding. + * If the worker process is not currently bound to this connection, + * MUST forward to the correct worker process */ if (conn->close_actively) { - /** - * The worker process is not currently bound to this connection, - * and needs to be forwarded to the correct worker process - */ - if (server_->is_hash_dispatch_mode()) { - int worker_id = server_->schedule_worker(conn->fd, nullptr); - if (worker_id != (int) SwooleG.process_id) { - worker = server_->get_worker(worker_id); - goto _notify; - } else { - goto _close; - } - } else if (!server_->is_worker()) { - worker = server_->get_worker(conn->fd % server_->worker_num); - _notify: - ev.type = SW_SERVER_EVENT_CLOSE; - ev.fd = session_id; - ev.reactor_id = conn->reactor_id; - return server_->send_to_worker_from_worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER) > 0; + bool hash = server_->is_hash_dispatch_mode(); + int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; + if (server_->is_worker() && (!hash || worker_id == (int) swoole_get_process_id())) { + goto _close; } + worker = server_->get_worker(worker_id); + ev.type = SW_SERVER_EVENT_CLOSE; + ev.fd = session_id; + ev.reactor_id = conn->reactor_id; + return server_->send_to_worker_from_worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER) > 0; } _close: @@ -305,7 +397,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; } else if (!(conn->close_force || conn->close_reset) && conn->closed) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 78006dbac3b..1f5a3862d14 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -20,58 +20,29 @@ namespace swoole { using network::Socket; -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker); static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls); -#endif - -static bool Server_is_single(Server *serv) { - return serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && - serv->user_worker_list.empty(); -} - -int Server::create_reactor_processes() { - reactor_num = worker_num; - connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); - return SW_ERR; - } - return SW_OK; -} - -void Server::destroy_reactor_processes() { - sw_free(connection_list); -} - int Server::start_reactor_processes() { - single_thread = 1; + single_thread = true; // listen TCP if (have_stream_sock == 1) { for (auto ls : ports) { - if (ls->is_dgram()) { - continue; - } -#ifdef HAVE_REUSEPORT - if (enable_reuse_port) { - if (::close(ls->socket->fd) < 0) { - swoole_sys_warning("close(%d) failed", ls->socket->fd); - } - delete ls->socket; - ls->socket = nullptr; - continue; - } else + if (ls->is_stream()) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (!enable_reuse_port) { #endif - { - // listen server socket - if (ls->listen() < 0) { - return SW_ERR; + // listen server socket + if (ls->listen() < 0) { + return SW_ERR; + } +#if defined(__linux__) && defined(HAVE_REUSEPORT) + } else { + ls->close_socket(); } +#endif } } } @@ -89,8 +60,8 @@ int Server::start_reactor_processes() { gs->event_workers.ptr = this; gs->event_workers.max_wait_time = max_wait_time; gs->event_workers.use_msgqueue = 0; - gs->event_workers.main_loop = ReactorProcess_loop; - gs->event_workers.onWorkerNotFound = Server::wait_other_worker; + gs->event_workers.main_loop = reactor_process_main_loop; + gs->event_workers.onWorkerNotFound = wait_other_worker; memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); gs->event_workers.workers = workers; @@ -105,81 +76,17 @@ int Server::start_reactor_processes() { return SW_ERR; } - // single worker - if (Server_is_single(this)) { - int retval = ReactorProcess_loop(&gs->event_workers, &gs->event_workers.workers[0]); + if (is_single_worker()) { + Worker *worker = &gs->event_workers.workers[0]; + SwooleWG.worker = worker; + int retval = reactor_process_main_loop(&gs->event_workers, worker); if (retval == SW_OK) { gs->event_workers.destroy(); } return retval; } - SW_LOOP_N(worker_num) { - create_worker(&gs->event_workers.workers[i]); - } - - // task workers - if (task_worker_num > 0) { - if (create_task_workers() < 0) { - return SW_ERR; - } - if (gs->task_workers.start() < 0) { - return SW_ERR; - } - } - - // create user worker process - if (!user_worker_list.empty()) { - user_workers = (Worker *) sw_shm_calloc(get_user_worker_num(), sizeof(Worker)); - if (user_workers == nullptr) { - swoole_sys_warning("gmalloc[server->user_workers] failed"); - return SW_ERR; - } - for (auto worker : user_worker_list) { - /** - * store the pipe object - */ - if (worker->pipe_object) { - store_pipe_fd(worker->pipe_object); - } - spawn_user_worker(worker); - } - } - - /** - * manager process is the same as the master process - */ - SwooleG.pid = gs->manager_pid = getpid(); - SwooleG.process_type = SW_PROCESS_MANAGER; - - /** - * manager process can not use signalfd - */ - SwooleG.use_signalfd = 0; - - gs->event_workers.onWorkerMessage = read_worker_message; - gs->event_workers.start(); - - init_signal_handler(); - - if (onManagerStart) { - onManagerStart(this); - } - - gs->event_workers.wait(); - gs->event_workers.shutdown(); - - kill_user_workers(); - - if (onManagerStop) { - onManagerStop(this); - } - - SW_LOOP_N(worker_num) { - destroy_worker(&gs->event_workers.workers[i]); - } - - return SW_OK; + return start_manager_process(); } static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { @@ -226,7 +133,7 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { - serv->call_command_handler(serv->message_bus, SwooleWG.worker->id, serv->get_worker(0)->pipe_master); + serv->call_command_handler(serv->message_bus, sw_worker()->id, serv->get_worker(0)->pipe_master); break; } case SW_SERVER_EVENT_COMMAND_RESPONSE: { @@ -244,21 +151,14 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { return SW_OK; } -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { +int Server::reactor_process_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - - SwooleG.process_type = SW_PROCESS_WORKER; SwooleG.pid = getpid(); - - SwooleG.process_id = worker->id; - if (serv->max_request > 0) { - SwooleWG.run_always = false; - } - SwooleWG.max_request = serv->max_request; - SwooleWG.worker = worker; SwooleTG.id = 0; + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); - serv->init_worker(worker); + serv->init_event_worker(worker); if (!SwooleTG.reactor) { if (swoole_event_init(0) < 0) { @@ -272,15 +172,24 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } + serv->worker_signal_init(); + + serv->gs->connection_nums[worker->id] = 0; + for (auto ls : serv->ports) { -#ifdef HAVE_REUSEPORT +#if defined(__linux__) and defined(HAVE_REUSEPORT) if (ls->is_stream() && serv->enable_reuse_port) { - if (ReactorProcess_reuse_port(ls) < 0) { + if (ls->create_socket(serv) < 0) { swoole_event_free(); return SW_ERR; } + + if (ls->listen() < 0) { + return SW_ERR; + } } #endif + ls->gs->connection_nums[worker->id] = 0; if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { return SW_ERR; } @@ -288,20 +197,13 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { reactor->id = worker->id; reactor->ptr = serv; - -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swoole_signalfd_setup(SwooleTG.reactor); - } -#endif - reactor->max_socket = serv->get_max_connection(); - reactor->close = Server::close_connection; + reactor->close = close_connection; // set event handler // connect - reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); + reactor->set_handler(SW_FD_STREAM_SERVER, accept_connection); // close reactor->default_error_handler = ReactorProcess_onClose; // pipe @@ -332,22 +234,20 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { serv->init_reactor(reactor); if (worker->id == 0) { - SwooleTG.update_time = 1; - if (serv->onStart) { + serv->gs->master_pid = getpid(); + if (serv->onStart && !serv->gs->called_onStart) { + serv->gs->called_onStart = 1; serv->onStart(serv); } } - /** - * 1 second timer - */ - if ((serv->master_timer = swoole_timer_add(1000, true, Server::timer_callback, serv)) == nullptr) { + if ((serv->master_timer = swoole_timer_add(1000L, true, timer_callback, serv)) == nullptr) { _fail: swoole_event_free(); return SW_ERR; } - serv->worker_start_callback(); + serv->worker_start_callback(worker); /** * for heartbeat check @@ -370,15 +270,15 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { /** * call internal serv hooks */ - if (serv->hooks[Server::HOOK_WORKER_CLOSE]) { + if (serv->isset_hook(HOOK_WORKER_CLOSE)) { void *hook_args[2]; hook_args[0] = serv; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; - serv->call_hook(Server::HOOK_WORKER_CLOSE, hook_args); + hook_args[1] = (void *) (uintptr_t) worker->id; + serv->call_hook(HOOK_WORKER_CLOSE, hook_args); } swoole_event_free(); - serv->worker_stop_callback(); + serv->worker_stop_callback(worker); return retval; } @@ -397,6 +297,11 @@ static int ReactorProcess_onClose(Reactor *reactor, Event *event) { if (conn->close_queued) { return Server::close_connection(reactor, event->socket); } else { + /** + * peer_closed indicates that the client has closed the connection + * and the connection is no longer available. + */ + conn->peer_closed = 1; return serv->notify(conn, SW_SERVER_EVENT_CLOSE) ? SW_OK : SW_ERR; } } else { @@ -433,21 +338,4 @@ static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) { ReactorProcess_onClose(reactor, ¬ify_ev); }); } - -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls) { - ls->socket = swoole::make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket->set_reuse_port() < 0) { - ls->socket->free(); - return SW_ERR; - } - if (ls->socket->bind(ls->host, &ls->port) < 0) { - ls->socket->free(); - return SW_ERR; - } - return ls->listen(); -} -#endif - } // namespace swoole diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index d00d1f44cc4..2c1a5e9ae66 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -10,14 +10,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_server.h" #include "swoole_memory.h" #include "swoole_hash.h" -#include "swoole_client.h" #include "swoole_util.h" #include @@ -27,15 +26,12 @@ using std::unordered_map; namespace swoole { using namespace network; -static void ReactorThread_loop(Server *serv, int reactor_id); static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev); static int ReactorThread_onRead(Reactor *reactor, Event *ev); static int ReactorThread_onWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event); static int ReactorThread_onClose(Reactor *reactor, Event *event); -static void ReactorThread_onStreamResponse(Stream *stream, const char *data, uint32_t length); -static void ReactorThread_shutdown(Reactor *reactor); static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode); #ifdef SW_USE_OPENSSL @@ -87,38 +83,12 @@ static inline ReturnCode ReactorThread_verify_ssl_state(Reactor *reactor, Listen } #endif -static void ReactorThread_onStreamResponse(Stream *stream, const char *data, uint32_t length) { - SendData response; - Server *serv = (Server *) stream->private_data; - Connection *conn = (Connection *) stream->private_data_2; - SessionId session_id = stream->private_data_fd; - - if (!conn->active || session_id != conn->session_id) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); - return; - } - if (data == nullptr) { - Event _ev = {}; - _ev.fd = conn->fd; - _ev.socket = conn->socket; - sw_reactor()->trigger_close_event(&_ev); - return; - } - - DataHead *pkg_info = (DataHead *) data; - response.info.fd = conn->session_id; - response.info.type = pkg_info->type; - response.info.len = length - sizeof(DataHead); - response.data = data + sizeof(DataHead); - serv->send_to_connection(&response); -} - /** * for udp */ static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) { int fd = event->fd; - int ret; + ssize_t ret; Server *serv = (Server *) reactor->ptr; Connection *server_sock = serv->get_connection(fd); @@ -215,10 +185,15 @@ int Server::close_connection(Reactor *reactor, Socket *socket) { } sw_atomic_fetch_add(&serv->gs->close_count, 1); - sw_atomic_fetch_sub(&serv->gs->connection_num, 1); - sw_atomic_fetch_add(&port->gs->close_count, 1); - sw_atomic_fetch_sub(&port->gs->connection_num, 1); + + if (serv->is_base_mode()) { + sw_atomic_fetch_sub(&serv->gs->connection_nums[reactor->id], 1); + sw_atomic_fetch_sub(&port->gs->connection_nums[reactor->id], 1); + } else { + sw_atomic_fetch_sub(&serv->gs->connection_num, 1); + sw_atomic_fetch_sub(&port->gs->connection_num, 1); + } swoole_trace("Close Event.fd=%d|from=%d", socket->fd, reactor->id); @@ -326,7 +301,7 @@ static int ReactorThread_onClose(Reactor *reactor, Event *event) { } } -static void ReactorThread_shutdown(Reactor *reactor) { +void ReactorThread::shutdown(Reactor *reactor) { Server *serv = (Server *) reactor->ptr; // stop listen UDP Port if (serv->have_dgram_sock == 1) { @@ -335,11 +310,26 @@ static void ReactorThread_shutdown(Reactor *reactor) { if (ls->socket->fd % serv->reactor_num != reactor->id) { continue; } - reactor->del(ls->socket); + if (!ls->socket->removed) { + reactor->del(ls->socket); + } } } } + if (serv->is_thread_mode()) { + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_worker(reactor->id)); + reactor->del(socket); + } + + SW_LOOP_N(serv->worker_num) { + if (i % serv->reactor_num != reactor->id) { + continue; + } + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i)); + reactor->del(socket); + } + serv->foreach_connection([serv, reactor](Connection *conn) { if (conn->fd % serv->reactor_num != reactor->id) { return; @@ -349,9 +339,46 @@ static void ReactorThread_shutdown(Reactor *reactor) { } }); + if (serv->is_thread_mode()) { + serv->stop_async_worker(serv->get_worker(reactor->id)); + } + reactor->set_wait_exit(true); } +int ReactorThread::close_connection(Reactor *reactor, SessionId session_id) { + Server *serv = (Server *) reactor->ptr; + Connection *conn = serv->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "force close connection failed, session#%ld does not exist", + session_id); + return SW_OK; + } + + if (serv->disable_notify || conn->close_force) { + return Server::close_connection(reactor, conn->socket); + } + +#ifdef SW_USE_OPENSSL + /** + * SSL connections that have not completed the handshake, + * do not need to notify the workers, just close + */ + if (conn->ssl && !conn->ssl_ready) { + return Server::close_connection(reactor, conn->socket); + } +#endif + conn->close_force = 1; + Event _ev = {}; + _ev.fd = conn->fd; + _ev.socket = conn->socket; + reactor->trigger_close_event(&_ev); + + return SW_OK; +} + /** * receive data from worker process pipe */ @@ -360,65 +387,30 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { Server *serv = (Server *) reactor->ptr; ReactorThread *thread = serv->get_thread(reactor->id); -#ifdef SW_REACTOR_RECV_AGAIN - while (1) -#endif - { + SW_LOOP { PipeBuffer *resp = thread->message_bus.get_buffer(); ssize_t n = thread->message_bus.read_with_buffer(ev->socket); if (n <= 0) { return n; } - /** - * connection incoming - */ if (resp->info.type == SW_SERVER_EVENT_INCOMING) { - Connection *conn = serv->get_connection_by_session_id(resp->info.fd); - if (serv->connection_incoming(reactor, conn) < 0) { - return reactor->close(reactor, conn->socket); + Connection *conn = serv->get_connection_verify_no_ssl(resp->info.fd); + if (conn && serv->connection_incoming(reactor, conn) < 0) { + reactor->close(reactor, conn->socket); } } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { - return serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); + serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_RESPONSE) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); - return SW_OK; - } - /** - * server shutdown - */ - else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - ReactorThread_shutdown(reactor); + } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { + thread->shutdown(reactor); + } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { + serv->onFinish(serv, (EventData *) resp); + } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { + serv->onPipeMessage(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_CLOSE_FORCE) { - SessionId session_id = resp->info.fd; - Connection *conn = serv->get_connection_verify_no_ssl(session_id); - - if (!conn) { - swoole_error_log(SW_LOG_NOTICE, - SW_ERROR_SESSION_NOT_EXIST, - "force close connection failed, session#%ld does not exist", - session_id); - return SW_OK; - } - - if (serv->disable_notify || conn->close_force) { - return Server::close_connection(reactor, conn->socket); - } - -#ifdef SW_USE_OPENSSL - /** - * SSL connections that have not completed the handshake, - * do not need to notify the workers, just close - */ - if (conn->ssl && !conn->ssl_ready) { - return Server::close_connection(reactor, conn->socket); - } -#endif - conn->close_force = 1; - Event _ev = {}; - _ev.fd = conn->fd; - _ev.socket = conn->socket; - reactor->trigger_close_event(&_ev); + thread->close_connection(reactor, resp->info.fd); } else { PacketPtr packet = thread->message_bus.get_packet(); _send.info = resp->info; @@ -443,7 +435,7 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { while (!Buffer::empty(buffer)) { BufferChunk *chunk = buffer->front(); - EventData *send_data = (EventData *) chunk->value.ptr; + EventData *send_data = (EventData *) chunk->value.str; // server actively closed connection, should discard the data if (Server::is_stream_event(send_data->info.type)) { @@ -470,9 +462,9 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { } } - ret = ev->socket->send(chunk->value.ptr, chunk->length, 0); + ret = ev->socket->send(chunk->value.str, chunk->length, 0); if (ret < 0) { - return (ev->socket->catch_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; + return (ev->socket->catch_write_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; } else { buffer->pop(); } @@ -499,10 +491,6 @@ void Server::init_reactor(Reactor *reactor) { // Read reactor->set_handler(SW_FD_SESSION | SW_EVENT_READ, ReactorThread_onRead); - if (dispatch_mode == DISPATCH_STREAM) { - Client::init_reactor(reactor); - } - // listen the all tcp port for (auto port : ports) { if (port->is_dgram() @@ -576,7 +564,8 @@ static int ReactorThread_onRead(Reactor *reactor, Event *event) { } if (serv->is_process_mode() && serv->max_queued_bytes && conn->recv_queued_bytes > serv->max_queued_bytes) { conn->waiting_time = 1; - conn->timer = swoole_timer_add(conn->waiting_time, false, ReactorThread_resume_data_receiving, event->socket); + conn->timer = + swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, event->socket); if (conn->timer) { reactor->remove_read_event(event->socket); } @@ -667,23 +656,6 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { return SW_OK; } -int Server::create_reactor_threads() { - /** - * init reactor thread pool - */ - reactor_threads = new ReactorThread[reactor_num](); - /** - * alloc the memory for connection_list - */ - connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_error("calloc[1] failed"); - return SW_ERR; - } - reactor_pipe_num = worker_num / reactor_num; - return SW_OK; -} - /** * [master] */ @@ -694,12 +666,6 @@ int Server::start_reactor_threads() { Reactor *reactor = sw_reactor(); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swoole_signalfd_setup(reactor); - } -#endif - for (auto iter = ports.begin(); iter != ports.end(); iter++) { auto port = *iter; if (port->is_dgram()) { @@ -730,7 +696,11 @@ int Server::start_reactor_threads() { } SW_LOOP_N(reactor_num) { - get_thread(i)->thread = std::thread(ReactorThread_loop, this, i); + get_thread(i)->thread = std::thread([=]() { + swoole_thread_init(); + reactor_thread_main_loop(this, i); + swoole_thread_clean(); + }); } _init_master_thread: @@ -742,7 +712,7 @@ int Server::start_reactor_threads() { start_heartbeat_thread(); } - return start_master_thread(); + return start_master_thread(reactor); } int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { @@ -751,11 +721,6 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { reactor->wait_exit = 0; reactor->max_socket = serv->get_max_connection(); reactor->close = Server::close_connection; - - reactor->set_exit_condition(Reactor::EXIT_CONDITION_DEFAULT, [this](Reactor *reactor, size_t &event_num) -> bool { - return event_num == (size_t) pipe_num; - }); - reactor->default_error_handler = ReactorThread_onClose; reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorThread_onPipeRead); @@ -788,20 +753,23 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } serv->init_reactor(reactor); + serv->init_pipe_sockets(&message_bus); - int max_pipe_fd = serv->get_worker(serv->worker_num - 1)->pipe_master->fd + 2; - pipe_sockets = (Socket *) sw_calloc(max_pipe_fd, sizeof(Socket)); - if (!pipe_sockets) { - swoole_sys_error("calloc(%d, %ld) failed", max_pipe_fd, sizeof(Socket)); - return SW_ERR; + if (serv->is_thread_mode()) { + Worker *worker = serv->get_worker(reactor_id); + serv->init_event_worker(worker); + auto pipe_worker = message_bus.get_pipe_socket(worker->pipe_worker); + reactor->add(pipe_worker, SW_EVENT_READ); } if (serv->pipe_command) { - pipe_command = make_socket(serv->pipe_command->get_socket(false)->get_fd(), SW_FD_PIPE); + auto pipe_socket = serv->pipe_command->get_socket(false); + message_bus.init_pipe_socket(pipe_socket); + pipe_command = message_bus.get_pipe_socket(pipe_socket); pipe_command->buffer_size = UINT_MAX; } - message_bus.set_id_generator([serv]() { return sw_atomic_fetch_add(&serv->gs->pipe_packet_msg_id, 1); }); + message_bus.set_id_generator(serv->msg_id_generator); message_bus.set_buffer_size(serv->ipc_max_size); message_bus.set_always_chunked_transfer(); if (!message_bus.alloc_buffer()) { @@ -809,89 +777,60 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } SW_LOOP_N(serv->worker_num) { - int pipe_fd = serv->workers[i].pipe_master->fd; - Socket *socket = &pipe_sockets[pipe_fd]; - - socket->fd = pipe_fd; - socket->fd_type = SW_FD_PIPE; - socket->buffer_size = UINT_MAX; - if (i % serv->reactor_num != reactor_id) { continue; } - - socket->set_nonblock(); - + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i)); if (reactor->add(socket, SW_EVENT_READ) < 0) { return SW_ERR; } + /** + * It will only send data to the notify pipeline synchronously, + * which is thread-safe and does not require separate memory + */ if (notify_pipe == nullptr) { notify_pipe = serv->workers[i].pipe_worker; } - pipe_num++; } return SW_OK; } -/** - * ReactorThread main Loop - */ -static void ReactorThread_loop(Server *serv, int reactor_id) { +void ReactorThread::clean() { + message_bus.free_buffer(); +} + +void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; + ReactorThread *thread = serv->get_thread(reactor_id); + thread->id = reactor_id; + SwooleTG.message_bus = &thread->message_bus; if (swoole_event_init(0) < 0) { return; } - ReactorThread *thread = serv->get_thread(reactor_id); - thread->id = reactor_id; - Reactor *reactor = sw_reactor(); - -#ifdef HAVE_CPU_AFFINITY - // cpu affinity setting - if (serv->open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - if (serv->cpu_affinity_available_num) { - CPU_SET(serv->cpu_affinity_available[reactor_id % serv->cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(reactor_id % SW_CPU_NUM, &cpu_set); - } - - if (0 != pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set)) { - swoole_sys_warning("pthread_setaffinity_np() failed"); - } + if (serv->is_thread_mode()) { + serv->call_worker_start_callback(serv->get_worker(reactor_id)); } -#endif - - swoole_signal_block_all(); + Reactor *reactor = sw_reactor(); if (thread->init(serv, reactor, reactor_id) < 0) { return; } // wait other thread -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_wait(&serv->reactor_thread_barrier); -#else - SW_START_SLEEP; -#endif + if (serv->is_process_mode()) { + serv->reactor_thread_barrier.wait(); + } // main loop swoole_event_wait(); - sw_free(thread->pipe_sockets); - if (thread->pipe_command) { - thread->pipe_command->fd = -1; - delete thread->pipe_command; + if (serv->is_thread_mode()) { + serv->call_worker_stop_callback(serv->get_worker(reactor_id)); } + thread->clean(); } static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) { @@ -902,7 +841,7 @@ static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) if (conn->waiting_time != 1024) { conn->waiting_time *= 2; } - conn->timer = swoole_timer_add(conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket); + conn->timer = swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket); if (conn->timer) { return; } @@ -915,7 +854,7 @@ static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) /** * dispatch request data [only data frame] */ -int Server::dispatch_task(Protocol *proto, Socket *_socket, const char *data, uint32_t length) { +int Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData *rdata) { Server *serv = (Server *) proto->private_data_2; SendData task; @@ -925,74 +864,42 @@ int Server::dispatch_task(Protocol *proto, Socket *_socket, const char *data, ui sw_memset_zero(&task.info, sizeof(task.info)); task.info.server_fd = conn->server_fd; task.info.reactor_id = conn->reactor_id; - task.info.ext_flags = proto->ext_flags; - proto->ext_flags = 0; + task.info.ext_flags = rdata->info.ext_flags; task.info.type = SW_SERVER_EVENT_RECV_DATA; task.info.time = conn->last_recv_time; - int return_code = SW_OK; + swoole_trace("dispatch task, size=%u bytes", rdata->info.len); - swoole_trace("send string package, size=%ld bytes", (long) length); + task.info.fd = conn->fd; + task.info.len = rdata->info.len; + task.data = rdata->data; - if (serv->stream_socket_file) { - Stream *stream = Stream::create(serv->stream_socket_file, 0, SW_SOCK_UNIX_STREAM); - if (!stream) { - return_code = SW_ERR; - goto _return; - } - stream->response = ReactorThread_onStreamResponse; - stream->private_data = serv; - stream->private_data_2 = conn; - stream->private_data_fd = conn->session_id; - stream->set_max_length(port->protocol.package_max_length); - - task.info.fd = conn->session_id; + if (rdata->info.len > 0) { + sw_atomic_fetch_add(&conn->recv_queued_bytes, rdata->info.len); + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, len=%d, qb=%d", + conn->session_id, + rdata->info.len, + conn->recv_queued_bytes); + } - if (stream->send((char *) &task.info, sizeof(task.info)) < 0) { - _cancel: - stream->cancel = 1; - delete stream; - return_code = SW_ERR; - goto _return; - } - if (stream->send(data, length) < 0) { - goto _cancel; + if (!serv->factory->dispatch(&task)) { + if (rdata->info.len > 0) { + sw_atomic_fetch_sub(&conn->recv_queued_bytes, rdata->info.len); } + return SW_ERR; } else { - task.info.fd = conn->fd; - task.info.len = length; - task.data = data; - if (length > 0) { - sw_atomic_fetch_add(&conn->recv_queued_bytes, length); - swoole_trace_log( - SW_TRACE_SERVER, "session_id=%ld, len=%d, qb=%d", conn->session_id, length, conn->recv_queued_bytes); - } - if (!serv->factory->dispatch(&task)) { - return_code = SW_ERR; - if (length > 0) { - sw_atomic_fetch_sub(&conn->recv_queued_bytes, length); - } - } - } - -_return: - if (return_code == SW_OK) { if (serv->is_process_mode()) { ReactorThread *thread = serv->get_thread(conn->reactor_id); thread->dispatch_count++; } sw_atomic_fetch_add(&serv->gs->dispatch_count, 1); sw_atomic_fetch_add(&port->gs->dispatch_count, 1); + return SW_OK; } - - return return_code; } -void Server::join_reactor_thread() { - if (single_thread) { - return; - } - ReactorThread *thread; +void Server::join_heartbeat_thread() { /** * Shutdown heartbeat thread */ @@ -1004,11 +911,19 @@ void Server::join_reactor_thread() { // wait thread heartbeat_thread.join(); } - /** - * kill threads - */ +} + +void Server::join_reactor_thread() { + if (single_thread) { + return; + } + + if (heartbeat_check_interval > 0) { + join_heartbeat_thread(); + } + for (int i = 0; i < reactor_num; i++) { - thread = get_thread(i); + ReactorThread *thread = get_thread(i); if (thread->notify_pipe) { DataHead ev = {}; ev.type = SW_SERVER_EVENT_SHUTDOWN; @@ -1025,21 +940,12 @@ void Server::join_reactor_thread() { } } -void Server::destroy_reactor_threads() { - sw_shm_free(connection_list); - delete[] reactor_threads; - - if (gs->event_workers.message_box) { - gs->event_workers.message_box->destroy(); - } -} - void Server::start_heartbeat_thread() { heartbeat_thread = std::thread([this]() { swoole_signal_block_all(); SwooleTG.type = THREAD_HEARTBEAT; - SwooleTG.id = reactor_num; + SwooleTG.id = reactor_num + 1; while (running) { double now = microtime(); diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index e745fb032a1..e0015bfad23 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -10,11 +10,12 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_static_handler.h" +#include "swoole_util.h" #include #include @@ -29,7 +30,7 @@ bool StaticHandler::is_modified(const std::string &date_if_modified_since) { return false; } - struct tm tm3; + struct tm tm3 {}; memcpy(date_tmp, date_if_modified_since.c_str(), date_if_modified_since.length()); date_tmp[date_if_modified_since.length()] = 0; @@ -44,7 +45,30 @@ bool StaticHandler::is_modified(const std::string &date_if_modified_since) { } else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) { date_format = SW_HTTP_ASCTIME_DATE; } - return date_format && mktime(&tm3) - (int) serv->timezone_ >= get_file_mtime(); + return date_format && mktime(&tm3) - (time_t) serv->timezone_ >= get_file_mtime(); +} + +bool StaticHandler::is_modified_range(const std::string &date_range) { + if (date_range.empty()) { + return false; + } + + struct tm tm3 {}; + + const char *date_format = nullptr; + + if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_GMT, &tm3) != nullptr) { + date_format = SW_HTTP_RFC1123_DATE_GMT; + } else if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_UTC, &tm3) != nullptr) { + date_format = SW_HTTP_RFC1123_DATE_UTC; + } else if (strptime(date_range.c_str(), SW_HTTP_RFC850_DATE, &tm3) != nullptr) { + date_format = SW_HTTP_RFC850_DATE; + } else if (strptime(date_range.c_str(), SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) { + date_format = SW_HTTP_ASCTIME_DATE; + } + time_t file_mtime = get_file_mtime(); + struct tm *tm_file_mtime = gmtime(&file_mtime); + return date_format && mktime(&tm3) != mktime(tm_file_mtime); } std::string StaticHandler::get_date() { @@ -63,8 +87,18 @@ std::string StaticHandler::get_date_last_modified() { return std::string(date_last_modified); } +bool StaticHandler::get_absolute_path() { + char abs_path[PATH_MAX]; + if (!realpath(filename, abs_path)) { + return false; + } + strncpy(filename, abs_path, sizeof(abs_path)); + l_filename = strlen(filename); + return true; +} + bool StaticHandler::hit() { - char *p = task.filename; + char *p = filename; const char *url = request_url.c_str(); size_t url_length = request_url.length(); /** @@ -78,13 +112,14 @@ bool StaticHandler::hit() { size_t n = params ? params - url : url_length; const std::string &document_root = serv->get_document_root(); + const size_t l_document_root = document_root.length(); - memcpy(p, document_root.c_str(), document_root.length()); - p += document_root.length(); + memcpy(p, document_root.c_str(), l_document_root); + p += l_document_root; if (serv->locations->size() > 0) { for (auto i = serv->locations->begin(); i != serv->locations->end(); i++) { - if (swoole_strcasect(url, url_length, i->c_str(), i->size())) { + if (swoole_str_istarts_with(url, url_length, i->c_str(), i->size())) { last = true; } } @@ -93,8 +128,8 @@ bool StaticHandler::hit() { } } - if (document_root.length() + n >= PATH_MAX) { - return false; + if (l_document_root + n >= PATH_MAX) { + return catch_error(); } memcpy(p, url, n); @@ -105,53 +140,30 @@ bool StaticHandler::hit() { } dir_path = std::string(url, n); - l_filename = http_server::url_decode(task.filename, p - task.filename); - task.filename[l_filename] = '\0'; + l_filename = http_server::url_decode(filename, p - filename); + filename[l_filename] = '\0'; - if (swoole_strnpos(url, n, SW_STRL("..")) == -1) { - goto _detect_mime_type; + // The file does not exist + if (lstat(filename, &file_stat) < 0) { + return catch_error(); } - char real_path[PATH_MAX]; - if (!realpath(task.filename, real_path)) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - return false; - } - } - - if (real_path[document_root.length()] != '/') { - return false; - } - - if (swoole_streq(real_path, strlen(real_path), document_root.c_str(), document_root.length()) != 0) { - return false; - } - -// non-static file -_detect_mime_type: -// file does not exist -check_stat: - if (lstat(task.filename, &file_stat) < 0) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - return false; + // The filename is relative path, allows for the resolution of symbolic links. + // This path is formed by concatenating the document root and that is permitted for access. + if (is_absolute_path()) { + if (is_link()) { + // Use the realpath function to resolve a symbolic link to its actual path. + if (!get_absolute_path()) { + return catch_error(); + } + if (lstat(filename, &file_stat) < 0) { + return catch_error(); + } } - } - - if (S_ISLNK(file_stat.st_mode)) { - char buf[PATH_MAX]; - ssize_t byte = ::readlink(task.filename, buf, sizeof(buf) - 1); - if (byte <= 0) { - return false; + } else { + if (!get_absolute_path() || !is_located_in_document_root()) { + return catch_error(); } - buf[byte] = 0; - swoole_strlcpy(task.filename, buf, sizeof(task.filename)); - goto check_stat; } if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { @@ -162,73 +174,84 @@ bool StaticHandler::hit() { return true; } - if (!swoole::mime_type::exists(task.filename)) { + if (!mime_type::exists(filename) && !last) { return false; } - if (!S_ISREG(file_stat.st_mode)) { + if (!is_file()) { return false; } - task.length = get_filesize(); return true; } -size_t StaticHandler::get_index_page(std::set &files, char *buffer, size_t size) { - int ret = 0; - char *p = buffer; +bool StaticHandler::hit_index_file() { + if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { + if (!get_dir_files()) { + return false; + } + index_file = intersection(*serv->http_index_files, dir_files); - if (dir_path.back() != '/') { - dir_path.append("/"); + if (has_index_file() && !set_filename(index_file)) { + return false; + } + if (!has_index_file() && !is_enabled_auto_index()) { + return false; + } } + return true; +} - ret = sw_snprintf(p, - size - ret, - "\n" - "\n" - "\t\nIndex of %s" - "\n" - "\n

Index of %s


" - "\t
    \n", - dir_path.c_str(), - dir_path.c_str()); +size_t StaticHandler::make_index_page(String *buffer) { + get_dir_files(); - p += ret; + if (dir_path.back() != '/') { + dir_path.append("/"); + } - for (auto iter = files.begin(); iter != files.end(); iter++) { + buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, + "\n" + "\n" + "\t\nIndex of %s" + "\n" + "\n" SW_HTTP_PAGE_CSS "

    Index of %s

    " + "\t
      \n", + dir_path.c_str(), + dir_path.c_str()); + + for (auto iter = dir_files.begin(); iter != dir_files.end(); iter++) { if (*iter == "." || (dir_path == "/" && *iter == "..")) { continue; } - ret = sw_snprintf( - p, size - ret, "\t\t
    • %s
    • \n", dir_path.c_str(), (*iter).c_str(), (*iter).c_str()); - p += ret; + buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, + "\t\t
    • %s
    • \n", + dir_path.c_str(), + (*iter).c_str(), + (*iter).c_str()); } - ret = sw_snprintf(p, - size - ret, - "\t
    \n" - "
    Powered by Swoole\n" - "\n"); - - p += ret; + buffer->append(SW_STRL("\t
\n" SW_HTTP_POWER_BY "\n\n")); - return p - buffer; + return buffer->length; } -bool StaticHandler::get_dir_files(std::set &index_files) { - struct dirent *ptr; +bool StaticHandler::get_dir_files() { + if (!dir_files.empty()) { + return true; + } if (!is_dir()) { return false; } - DIR *dir = opendir(task.filename); + DIR *dir = opendir(filename); if (dir == nullptr) { return false; } + struct dirent *ptr; while ((ptr = readdir(dir)) != nullptr) { - index_files.insert(ptr->d_name); + dir_files.insert(ptr->d_name); } closedir(dir); @@ -236,46 +259,199 @@ bool StaticHandler::get_dir_files(std::set &index_files) { return true; } -bool StaticHandler::set_filename(std::string &filename) { - char *p = task.filename + l_filename; +bool StaticHandler::set_filename(const std::string &_filename) { + char *p = filename + l_filename; if (*p != '/') { *p = '/'; p += 1; } - memcpy(p, filename.c_str(), filename.length()); - p += filename.length(); + memcpy(p, _filename.c_str(), _filename.length()); + p += _filename.length(); *p = 0; - if (lstat(task.filename, &file_stat) < 0) { + if (lstat(filename, &file_stat) < 0) { return false; } - if (!S_ISREG(file_stat.st_mode)) { + if (!is_file()) { return false; } - task.length = get_filesize(); - return true; } + +void StaticHandler::parse_range(const char *range, const char *if_range) { + task_t _task{}; + _task.length = 0; + // range + if (range && '\0' != *range) { + const char *p = range; + // bytes= + if (!SW_STR_ISTARTS_WITH(p, strlen(range), "bytes=")) { + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + return; + } + p += 6; + size_t start, end, size = 0, cutoff = SIZE_MAX / 10, cutlim = SIZE_MAX % 10, suffix, + _content_length = get_filesize(); + content_length = 0; + for (;;) { + start = 0; + end = 0; + suffix = 0; + + while (*p == ' ') { + p++; + } + + if (*p != '-') { + if (*p < '0' || *p > '9') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p >= '0' && *p <= '9') { + if (start >= cutoff && (start > cutoff || (size_t) (*p - '0') > cutlim)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + start = start * 10 + (*p++ - '0'); + } + + while (*p == ' ') { + p++; + } + + if (*p++ != '-') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p == ' ') { + p++; + } + + if (*p == ',' || *p == '\0') { + end = _content_length; + goto found; + } + + } else { + suffix = 1; + p++; + } + + if (*p < '0' || *p > '9') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p >= '0' && *p <= '9') { + if (end >= cutoff && (end > cutoff || (size_t) (*p - '0') > cutlim)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + end = end * 10 + (*p++ - '0'); + } + + while (*p == ' ') { + p++; + } + + if (*p != ',' && *p != '\0' && *p != '\r') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + if (suffix) { + start = (end < _content_length) ? _content_length - end : 0; + end = _content_length - 1; + } + + if (end >= _content_length) { + end = _content_length; + + } else { + end++; + } + + found: + if (start < end) { + if (size > SIZE_MAX - (end - start)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + size += end - start; + _task.offset = start; + _task.length = end - start; + content_length += sw_snprintf(_task.part_header, + sizeof(_task.part_header), + "%s--%s\r\n" + "Content-Type: %s\r\n" + "Content-Range: bytes %zu-%zu/%zu\r\n\r\n", + tasks.empty() ? "" : "\r\n", + get_boundary(), + get_mimetype(), + _task.offset, + end - 1, + get_filesize()) + + _task.length; + tasks.push_back(_task); + } else if (start == 0) { + break; + } + + if (*p++ != ',' || '\r' == *p || '\0' == *p) { + break; + } + } + } + if (_task.length > 0) { + if (1 == tasks.size()) { + content_length = _task.length; + } else { + end_part = std::string("\r\n--") + get_boundary() + "--\r\n"; + content_length += end_part.size(); + } + status_code = SW_HTTP_PARTIAL_CONTENT; + } else { + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + } + // if-range + if (if_range) { + if (is_modified_range(if_range)) { + tasks.clear(); + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + status_code = SW_HTTP_OK; + } + } +} } // namespace http_server void Server::add_static_handler_location(const std::string &location) { if (locations == nullptr) { - locations = new std::unordered_set; + locations = std::make_shared>(); } - locations->insert(location); + locations->emplace(location); } void Server::add_static_handler_index_files(const std::string &file) { if (http_index_files == nullptr) { - http_index_files = new std::vector; + http_index_files = std::make_shared>(); } auto iter = std::find(http_index_files->begin(), http_index_files->end(), file); if (iter == http_index_files->end()) { - http_index_files->push_back(file); + http_index_files->emplace_back(file); } } } // namespace swoole diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index a5cfca046cd..424fbd992b9 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,9 +22,9 @@ using network::Socket; static void TaskWorker_signal_init(ProcessPool *pool); static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event); static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker); -static void TaskWorker_onStart(ProcessPool *pool, int worker_id); -static void TaskWorker_onStop(ProcessPool *pool, int worker_id); -static int TaskWorker_onTask(ProcessPool *pool, EventData *task); +static void TaskWorker_onStart(ProcessPool *pool, Worker *worker); +static void TaskWorker_onStop(ProcessPool *pool, Worker *worker); +static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task); /** * after pool->create, before pool->start @@ -48,11 +48,14 @@ void Server::init_task_workers() { if (task_ipc_mode == TASK_IPC_PREEMPTIVE) { pool->schedule_by_sysvmsg = true; } + SW_LOOP_N(task_worker_num) { + create_worker(&pool->workers[i]); + } } -static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { +static int TaskWorker_call_command_handler(ProcessPool *pool, Worker *worker, EventData *req) { Server *serv = (Server *) pool->ptr; - int command_id = req->info.server_fd; + int command_id = serv->get_command_id(req); auto iter = serv->command_handlers.find(command_id); if (iter == serv->command_handlers.end()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[%d]", command_id); @@ -68,8 +71,8 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { auto result = handler(serv, std::string(packet.data, packet.length)); SendData task{}; - task.info.fd = req->info.fd; - task.info.reactor_id = SwooleWG.worker->id; + task.info.fd = serv->get_task_id(req); + task.info.reactor_id = worker->id; task.info.server_fd = -1; task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; task.info.len = result.length(); @@ -78,27 +81,38 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { return serv->message_bus.write(serv->get_command_reply_socket(), &task) ? SW_OK : SW_ERR; } -static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { +static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) { int ret = SW_OK; Server *serv = (Server *) pool->ptr; serv->last_task = task; + worker->set_status_to_busy(); if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); + } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { + worker->shutdown(); + return SW_OK; } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { - ret = TaskWorker_call_command_handler(pool, task); + ret = TaskWorker_call_command_handler(pool, worker, task); } else { ret = serv->onTask(serv, task); + /** + * only server task as requests, + * do not increase the count for pipeline communication and command processing. + */ + worker->add_request_count(); } + worker->set_status_to_idle(); return ret; } bool Server::task_pack(EventData *task, const void *_data, size_t _length) { + task->info = {}; task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; - task->info.reactor_id = SwooleG.process_id; - task->info.time = swoole::microtime(); + task->info.reactor_id = swoole_get_process_id(); + task->info.time = microtime(); if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { memcpy(task->data, _data, _length); @@ -126,6 +140,59 @@ bool Server::task_pack(EventData *task, const void *_data, size_t _length) { return true; } +bool Server::task(EventData *_task, int *dst_worker_id, bool blocking) { + sw_atomic_fetch_add(&gs->tasking_num, 1); + + swResultCode retval; + if (blocking) { + retval = gs->task_workers.dispatch_blocking(_task, dst_worker_id); + } else { + retval = gs->task_workers.dispatch(_task, dst_worker_id); + } + + if (retval == SW_OK) { + sw_atomic_fetch_add(&gs->task_count, 1); + return true; + } + + sw_atomic_fetch_sub(&gs->tasking_num, 1); + return false; +} + +bool Server::task_sync(EventData *_task, int *dst_worker_id, double timeout) { + uint64_t notify; + EventData *task_result = get_task_result(); + sw_memset_zero(task_result, sizeof(*task_result)); + Pipe *pipe = task_notify_pipes.at(swoole_get_process_id()).get(); + network::Socket *task_notify_socket = pipe->get_socket(false); + TaskId task_id = get_task_id(_task); + + // clear history task + while (task_notify_socket->wait_event(0, SW_EVENT_READ) == SW_OK) { + if (task_notify_socket->read(¬ify, sizeof(notify)) <= 0) { + break; + } + } + + if (!task(_task, dst_worker_id, true)) { + return false; + } + + SW_LOOP { + if (task_notify_socket->wait_event((int) (timeout * 1000), SW_EVENT_READ) == SW_OK) { + if (pipe->read(¬ify, sizeof(notify)) > 0) { + if (get_task_id(task_result) != task_id) { + continue; + } + return true; + } + } + break; + } + + return false; +} + bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { if (!(task->info.ext_flags & SW_TASK_TMPFILE)) { packet->data = task->data; @@ -134,7 +201,7 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } PacketTask _pkg{}; - memcpy(&_pkg, task->data, sizeof(_pkg)); + memcpy(&_pkg, task->data, sizeof(_pkg) - 1); File fp(_pkg.tmpfile, O_RDONLY); if (!fp.ready()) { @@ -157,11 +224,10 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } static void TaskWorker_signal_init(ProcessPool *pool) { - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - + Server *serv = (Server *) pool->ptr; + if (serv->is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGUSR1, Server::worker_signal_handler); @@ -172,9 +238,9 @@ static void TaskWorker_signal_init(ProcessPool *pool) { #endif } -static void TaskWorker_onStart(ProcessPool *pool, int worker_id) { +static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - SwooleG.process_id = worker_id; + swoole_set_process_id(worker->id); /** * Make the task worker support asynchronous @@ -191,28 +257,16 @@ static void TaskWorker_onStart(ProcessPool *pool, int worker_id) { } TaskWorker_signal_init(pool); - serv->worker_start_callback(); + serv->worker_start_callback(worker); - Worker *worker = pool->get_worker(worker_id); - worker->start_time = ::time(nullptr); - worker->request_count = 0; - SwooleWG.worker = worker; - SwooleWG.worker->status = SW_WORKER_IDLE; - /** - * task_max_request - */ - if (pool->max_request > 0) { - SwooleWG.run_always = false; - SwooleWG.max_request = pool->get_max_request(); - } else { - SwooleWG.run_always = true; - } + worker->init(); + worker->set_max_request(pool->max_request, pool->max_request_grace); } -static void TaskWorker_onStop(ProcessPool *pool, int worker_id) { +static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); Server *serv = (Server *) pool->ptr; - serv->worker_stop_callback(); + serv->worker_stop_callback(worker); } /** @@ -221,16 +275,13 @@ static void TaskWorker_onStop(ProcessPool *pool, int worker_id) { static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { EventData task; ProcessPool *pool = (ProcessPool *) reactor->ptr; - Worker *worker = SwooleWG.worker; + Worker *worker = sw_worker(); Server *serv = (Server *) pool->ptr; if (event->socket->read(&task, sizeof(task)) > 0) { - worker->status = SW_WORKER_BUSY; - int retval = TaskWorker_onTask(pool, &task); - worker->status = SW_WORKER_IDLE; - worker->request_count++; + int retval = pool->onTask(pool, worker, &task); // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (worker->has_exceeded_max_request()) { serv->stop_async_worker(worker); } return retval; @@ -246,7 +297,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; Socket *socket = worker->pipe_worker; - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); socket->set_nonblock(); sw_reactor()->ptr = pool; @@ -265,23 +316,21 @@ static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) { /** * Send the task result to worker */ -int Server::reply_task_result(const char *data, size_t data_len, int flags, EventData *current_task) { - EventData buf; - sw_memset_zero(&buf.info, sizeof(buf.info)); +bool Server::finish(const char *data, size_t data_len, int flags, EventData *current_task) { if (task_worker_num < 1) { swoole_warning("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]"); - return SW_ERR; + return false; } if (current_task == nullptr) { current_task = last_task; } if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { swoole_warning("Server::task()/Server::finish() is not supported in onPipeMessage callback"); - return SW_ERR; + return false; } if (current_task->info.ext_flags & SW_TASK_NOREPLY) { swoole_warning("Server::finish() can only be used in the worker process"); - return SW_ERR; + return false; } uint16_t source_worker_id = current_task->info.reactor_id; @@ -289,16 +338,17 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even if (worker == nullptr) { swoole_warning("invalid worker_id[%d]", source_worker_id); - return SW_ERR; + return false; } - int ret; + ssize_t retval; // for swoole_server_task if (current_task->info.ext_flags & SW_TASK_NONBLOCK) { // write to file + EventData buf; if (!task_pack(&buf, data, data_len)) { swoole_warning("large task pack failed()"); - return SW_ERR; + return false; } // callback function if (current_task->info.ext_flags & SW_TASK_CALLBACK) { @@ -308,16 +358,16 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even } buf.info.ext_flags |= flags; buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; + buf.info.fd = get_task_id(current_task); if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) { uint32_t _len = htonl(data_len); - ret = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len)); - if (ret > 0) { - ret = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); + retval = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len)); + if (retval > 0) { + retval = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); } } else { - ret = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); + retval = send_to_worker_from_worker(worker, &buf, buf.size(), SW_PIPE_MASTER); } } else { uint64_t flag = 1; @@ -325,7 +375,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even /** * Use worker shm store the result */ - EventData *result = &(task_result[source_worker_id]); + EventData *result = &(task_results[source_worker_id]); Pipe *pipe = task_notify_pipes.at(source_worker_id).get(); // lock worker @@ -336,14 +386,15 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even char *_tmpfile = result->data + 4; File file(_tmpfile, O_APPEND | O_WRONLY); if (file.ready()) { + EventData buf; if (!task_pack(&buf, data, data_len)) { swoole_warning("large task pack failed()"); buf.info.len = 0; } buf.info.ext_flags |= flags; buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; - size_t bytes = sizeof(buf.info) + buf.info.len; + buf.info.fd = get_task_id(current_task); + size_t bytes = buf.size(); if (file.write_all(&buf, bytes) != bytes) { swoole_sys_warning("write(%s, %ld) failed", _tmpfile, bytes); } @@ -354,20 +405,20 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even // unlock worker worker->lock->unlock(); swoole_warning("large task pack failed()"); - return SW_ERR; + return false; } result->info.ext_flags |= flags; result->info.type = SW_SERVER_EVENT_FINISH; - result->info.fd = current_task->info.fd; + result->info.fd = get_task_id(current_task); } // unlock worker worker->lock->unlock(); while (1) { - ret = pipe->write(&flag, sizeof(flag)); + retval = pipe->write(&flag, sizeof(flag)); auto _sock = pipe->get_socket(true); - if (ret < 0 && _sock->catch_error(errno) == SW_WAIT) { + if (retval < 0 && _sock->catch_write_error(errno) == SW_WAIT) { if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) { continue; } @@ -375,13 +426,13 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even break; } } - if (ret < 0) { + if (retval < 0) { if (swoole_get_last_error() == EAGAIN || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) { - swoole_warning("send result to worker timed out"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, "send result to worker timed out"); } else { swoole_sys_warning("send result to worker failed"); } } - return ret; + return true; } } // namespace swoole diff --git a/src/server/thread.cc b/src/server/thread.cc new file mode 100644 index 00000000000..798845a91bb --- /dev/null +++ b/src/server/thread.cc @@ -0,0 +1,373 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_thread.h" + +#define SW_RELOAD_SLEEP_FOR 100000 + +namespace swoole { +using network::Socket; + +Factory *Server::create_thread_factory() { +#ifndef SW_THREAD + swoole_error("Thread support is not enabled, cannot create server with MODE_THREAD"); + return nullptr; +#endif + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + reactor_threads = new ReactorThread[reactor_num](); + reactor_pipe_num = 1; + return new ThreadFactory(this); +} + +void Server::destroy_thread_factory() { + sw_free(connection_list); + delete[] reactor_threads; +} + +ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { + threads_.resize(server_->get_all_worker_num() + 1); + reloading = false; + reload_all_workers = false; + cv_timeout_ms_ = -1; +} + +bool ThreadFactory::start() { + if (!server_->create_worker_pipes()) { + return false; + } + if (server_->task_worker_num > 0 && + (server_->create_task_workers() < 0 || server_->gs->task_workers.start_check() < 0)) { + return false; + } + if (server_->get_user_worker_num() > 0 && server_->create_user_workers() < 0) { + return false; + } + return true; +} + +bool ThreadFactory::shutdown() { + for (auto &thread : threads_) { + if (thread.joinable()) { + join_thread(thread); + } + } + if (server_->heartbeat_check_interval > 0) { + server_->join_heartbeat_thread(); + } + return true; +} + +ThreadFactory::~ThreadFactory() {} + +void ThreadFactory::at_thread_exit(Worker *worker) { + std::unique_lock _lock(lock_); + queue_.push(worker); + cv_.notify_one(); +} + +void ThreadFactory::create_message_bus() { + auto mb = new MessageBus(); + mb->set_id_generator(server_->msg_id_generator); + mb->set_buffer_size(server_->ipc_max_size); + mb->set_always_chunked_transfer(); + if (!mb->alloc_buffer()) { + throw std::bad_alloc(); + } + server_->init_pipe_sockets(mb); + SwooleTG.message_bus = mb; +} + +void ThreadFactory::destroy_message_bus() { + SwooleTG.message_bus->clear(); + delete SwooleTG.message_bus; + SwooleTG.message_bus = nullptr; +} + +template +void ThreadFactory::create_thread(int i, _Callable fn) { + threads_[i] = std::thread(fn); +} + +void ThreadFactory::join_thread(std::thread &thread) { + thread.join(); + if (server_->worker_thread_join) { + server_->worker_thread_join(thread.native_handle()); + } +} + +void ThreadFactory::spawn_event_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_EVENTWORKER; + worker->pid = swoole_thread_get_native_id(); + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_task_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_TASKWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + create_message_bus(); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_TASKWORKER; + worker->pid = swoole_thread_get_native_id(); + worker->set_status_to_idle(); + SwooleWG.worker = worker; + auto pool = &server_->gs->task_workers; + server_->worker_thread_start([=]() { + if (pool->onWorkerStart != nullptr) { + pool->onWorkerStart(pool, worker); + } + pool->main_loop(pool, worker); + if (pool->onWorkerStop != nullptr) { + pool->onWorkerStop(pool, worker); + } + }); + destroy_message_bus(); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_user_worker(WorkerId i) { + create_thread(i, [=]() { + Worker *worker = server_->get_worker(i); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + create_message_bus(); + worker->type = SW_PROCESS_USERWORKER; + worker->pid = swoole_thread_get_native_id(); + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); + destroy_message_bus(); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_manager_thread(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_MANAGER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + manager.id = i; + manager.type = SW_PROCESS_MANAGER; + + SwooleTG.timer_scheduler = [this](Timer *timer, long exec_msec) -> int { + cv_timeout_ms_ = exec_msec; + return SW_OK; + }; + + server_->worker_thread_start([=]() { + if (server_->onManagerStart) { + server_->onManagerStart(server_); + } + wait(); + if (server_->onManagerStop) { + server_->onManagerStop(server_); + } + }); + + if (server_->running) { + swoole_warning("Fatal Error: manager thread exits abnormally"); + } + + SwooleTG.timer_scheduler = nullptr; + }); +} + +void ThreadFactory::wait() { + while (server_->running) { + std::unique_lock _lock(lock_); + if (!queue_.empty()) { + Worker *exited_worker = queue_.front(); + queue_.pop(); + + std::thread &thread = threads_[exited_worker->id]; + int status_code = 0; + if (server_->worker_thread_get_exit_status) { + status_code = server_->worker_thread_get_exit_status(thread.native_handle()); + } + if (status_code != 0) { + ExitStatus exit_status(exited_worker->pid, status_code << 8); + server_->call_worker_error_callback(exited_worker, exit_status); + swoole_warning("worker(tid=%d, id=%d) abnormal exit, status=%d", + exit_status.get_pid(), + exited_worker->id, + exit_status.get_code()); + } + + join_thread(threads_[exited_worker->id]); + + switch (exited_worker->type) { + case SW_PROCESS_EVENTWORKER: + spawn_event_worker(exited_worker->id); + break; + case SW_PROCESS_TASKWORKER: + spawn_task_worker(exited_worker->id); + break; + case SW_PROCESS_USERWORKER: + spawn_user_worker(exited_worker->id); + break; + default: + abort(); + break; + } + _lock.unlock(); + } else { + if (cv_timeout_ms_ > 0) { + cv_.wait_for(_lock, std::chrono::milliseconds(cv_timeout_ms_)); + } else { + cv_.wait(_lock); + } + } + if (sw_timer()) { + sw_timer()->select(); + } + if (server_->running && reloading) { + reload(reload_all_workers); + } + } +} + +bool ThreadFactory::reload(bool _reload_all_workers) { + if (!server_->is_manager()) { + // Prevent duplicate submission of reload requests. + if (reloading) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + reloading = true; + reload_all_workers = _reload_all_workers; + std::unique_lock _lock(lock_); + cv_.notify_one(); + } else { + swoole_info("Server is reloading %s workers now", _reload_all_workers ? "all" : "task"); + if (server_->onBeforeReload) { + server_->onBeforeReload(server_); + } + SW_LOOP_N(server_->get_core_worker_num()) { + if (i < server_->worker_num && !_reload_all_workers) { + continue; + } + if (!server_->kill_worker(i, true)) { + return false; + } + SW_LOOP { + usleep(SW_RELOAD_SLEEP_FOR); + // This worker thread has exited, proceeding to terminate the next one. + if (threads_[i].joinable()) { + break; + } + } + } + reload_all_workers = false; + reloading = false; + if (server_->onAfterReload) { + server_->onAfterReload(server_); + } + } + + return true; +} + +int Server::start_worker_threads() { + ThreadFactory *_factory = dynamic_cast(factory); + + if (heartbeat_check_interval > 0) { + start_heartbeat_thread(); + } + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + _factory->spawn_task_worker(worker_num + i); + } + } + + SW_LOOP_N(worker_num) { + _factory->spawn_event_worker(i); + } + + if (!user_worker_list.empty()) { + for (size_t i = 0; i < user_worker_list.size(); i++) { + _factory->spawn_user_worker(task_worker_num + worker_num + i); + } + } + + int manager_thread_id = get_all_worker_num(); + _factory->spawn_manager_thread(manager_thread_id); + + if (swoole_event_init(0) < 0) { + return SW_ERR; + } + + Reactor *reactor = sw_reactor(); + for (auto iter = ports.begin(); iter != ports.end(); iter++) { + auto port = *iter; + if (port->is_dgram()) { + continue; + } + if (port->listen() < 0) { + swoole_event_free(); + return SW_ERR; + } + reactor->add(port->socket, SW_EVENT_READ); + } + + SwooleTG.id = reactor->id = manager_thread_id + 1; + store_listen_socket(); + + return start_master_thread(reactor); +} + +void Server::stop_worker_threads() { + DataHead event = {}; + event.type = SW_SERVER_EVENT_SHUTDOWN; + + SW_LOOP_N(worker_num) { + send_to_worker_from_worker(get_worker(i), &event, sizeof(event), SW_PIPE_MASTER); + } + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + send_to_worker_from_worker(get_worker(worker_num + i), &event, sizeof(event), SW_PIPE_MASTER); + } + } +} + +bool Server::reload_worker_threads(bool reload_all_workers) { + ThreadFactory *_factory = dynamic_cast(factory); + return _factory->reload(reload_all_workers); +} + +} // namespace swoole diff --git a/src/server/worker.cc b/src/server/worker.cc index 85055e6d457..0f6ca46074e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -22,27 +22,18 @@ #include "swoole_server.h" #include "swoole_memory.h" #include "swoole_msg_queue.h" -#include "swoole_client.h" #include "swoole_coroutine.h" -swoole::WorkerGlobal SwooleWG = {}; - namespace swoole { using namespace network; static int Worker_onPipeReceive(Reactor *reactor, Event *event); -static int Worker_onStreamAccept(Reactor *reactor, Event *event); -static int Worker_onStreamRead(Reactor *reactor, Event *event); -static int Worker_onStreamPackage(Protocol *proto, Socket *sock, const char *data, uint32_t length); -static int Worker_onStreamClose(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - + if (is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, SIG_IGN); swoole_signal_set(SIGUSR1, nullptr); @@ -57,18 +48,17 @@ void Server::worker_signal_init(void) { } void Server::worker_signal_handler(int signo) { - if (!SwooleG.running or !sw_server()) { + if (!SwooleG.running || !sw_server() || !sw_worker() || !sw_server()->is_running()) { return; } switch (signo) { case SIGTERM: - // Event worker if (swoole_event_is_available()) { - sw_server()->stop_async_worker(SwooleWG.worker); - } - // Task worker - else { - SwooleWG.shutdown = true; + // Event Worker + sw_server()->stop_async_worker(sw_worker()); + } else { + // Task Worker + sw_worker()->shutdown(); } break; // for test @@ -113,118 +103,34 @@ static sw_inline bool Worker_discard_data(Server *serv, Connection *conn, DataHe return true; } -static int Worker_onStreamAccept(Reactor *reactor, Event *event) { - Socket *sock = event->socket->accept(); - if (sock == nullptr) { - switch (errno) { - case EINTR: - case EAGAIN: - return SW_OK; - default: - swoole_sys_warning("accept() failed"); - return SW_OK; - } - } - - sock->fd_type = SW_FD_STREAM; - sock->socket_type = SW_SOCK_UNIX_STREAM; - - return reactor->add(sock, SW_EVENT_READ); -} - -static int Worker_onStreamRead(Reactor *reactor, Event *event) { - Socket *conn = event->socket; - Server *serv = (Server *) reactor->ptr; - Protocol *protocol = &serv->stream_protocol; - String *buffer; - - if (!event->socket->recv_buffer) { - if (serv->buffer_pool->empty()) { - buffer = new String(SW_BUFFER_SIZE_STD); - } else { - buffer = serv->buffer_pool->front(); - serv->buffer_pool->pop(); - } - event->socket->recv_buffer = buffer; - } else { - buffer = event->socket->recv_buffer; - } - - if (protocol->recv_with_length_protocol(conn, buffer) < 0) { - Worker_onStreamClose(reactor, event); - } - - return SW_OK; -} - -static int Worker_onStreamClose(Reactor *reactor, Event *event) { - Socket *sock = event->socket; - Server *serv = (Server *) reactor->ptr; - - sock->recv_buffer->clear(); - serv->buffer_pool->push(sock->recv_buffer); - sock->recv_buffer = nullptr; - - reactor->del(sock); - reactor->close(reactor, sock); - - if (serv->last_stream_socket == sock) { - serv->last_stream_socket = nullptr; - } - - return SW_OK; -} - -static int Worker_onStreamPackage(Protocol *proto, Socket *sock, const char *data, uint32_t length) { - Server *serv = (Server *) proto->private_data_2; - - SendData task{}; - memcpy(&task.info, data + proto->package_length_size, sizeof(task.info)); - task.info.len = length - (uint32_t) sizeof(task.info) - proto->package_length_size; - if (task.info.len > 0) { - task.data = (char *) (data + proto->package_length_size + sizeof(task.info)); - } - - serv->last_stream_socket = sock; - serv->message_bus.pass(&task); - serv->worker_accept_event(&serv->message_bus.get_buffer()->info); - serv->last_stream_socket = nullptr; - - int _end = 0; - swoole_event_write(sock, (void *) &_end, sizeof(_end)); - - return SW_OK; -} - typedef std::function TaskCallback; static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { RecvData recv_data; - auto packet = serv->message_bus.get_packet(); + auto packet = serv->get_worker_message_bus()->get_packet(); recv_data.info = *info; recv_data.info.len = packet.length; recv_data.data = packet.data; if (callback(serv, &recv_data) == SW_OK) { - worker->request_count++; + worker->add_request_count(); sw_atomic_fetch_add(&serv->gs->request_count, 1); } } void Server::worker_accept_event(DataHead *info) { - Worker *worker = SwooleWG.worker; - // worker busy - worker->status = SW_WORKER_BUSY; + Worker *worker = sw_worker(); + worker->set_status_to_busy(); switch (info->type) { case SW_SERVER_EVENT_RECV_DATA: { Connection *conn = get_connection_verify(info->fd); if (conn) { if (info->len > 0) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); sw_atomic_fetch_sub(&conn->recv_queued_bytes, packet.length); swoole_trace_log(SW_TRACE_SERVER, - "[Worker] session_id=%ld, len=%d, qb=%d", + "[Worker] session_id=%ld, len=%lu, qb=%d", conn->session_id, packet.length, conn->recv_queued_bytes); @@ -257,7 +163,7 @@ void Server::worker_accept_event(DataHead *info) { if (info->len > 0) { Connection *conn = get_connection_verify_no_ssl(info->fd); if (conn) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); conn->ssl_client_cert = new String(packet.data, packet.length); conn->ssl_client_cert_pid = SwooleG.pid; } @@ -282,36 +188,39 @@ void Server::worker_accept_event(DataHead *info) { break; } case SW_SERVER_EVENT_FINISH: { - onFinish(this, (EventData *) message_bus.get_buffer()); + onFinish(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_PIPE_MESSAGE: { - onPipeMessage(this, (EventData *) message_bus.get_buffer()); + onPipeMessage(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { call_command_handler(message_bus, worker->id, pipe_command->get_socket(false)); break; } + case SW_SERVER_EVENT_SHUTDOWN: { + stop_async_worker(worker); + break; + } default: swoole_warning("[Worker] error event[type=%d]", (int) info->type); break; } - // worker idle - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (worker->has_exceeded_max_request()) { stop_async_worker(worker); } } -void Server::worker_start_callback() { - if (SwooleG.process_id >= worker_num) { - SwooleG.process_type = SW_PROCESS_TASKWORKER; +void Server::worker_start_callback(Worker *worker) { + if (swoole_get_process_id() >= worker_num) { + swoole_set_process_type(SW_PROCESS_TASKWORKER); } else { - SwooleG.process_type = SW_PROCESS_WORKER; + swoole_set_process_type(SW_PROCESS_WORKER); } int is_root = !geteuid(); @@ -354,12 +263,12 @@ void Server::worker_start_callback() { } SW_LOOP_N(worker_num + task_worker_num) { - Worker *worker = get_worker(i); - if (SwooleG.process_id == i) { + if (worker->id == i) { continue; } - if (is_worker() && worker->pipe_master) { - worker->pipe_master->set_nonblock(); + Worker *other_worker = get_worker(i); + if (is_worker() && other_worker->pipe_master) { + other_worker->pipe_master->set_nonblock(); } } @@ -367,48 +276,117 @@ void Server::worker_start_callback() { sw_logger()->reopen(); } - SwooleWG.worker = get_worker(SwooleG.process_id); - SwooleWG.worker->status = SW_WORKER_IDLE; - -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && SwooleTG.reactor && SwooleG.signal_fd == 0) { - swoole_signalfd_setup(SwooleTG.reactor); - } -#endif + worker->set_status_to_idle(); if (is_process_mode()) { sw_shm_protect(session_list, PROT_READ); } - call_worker_start_callback(SwooleWG.worker); + call_worker_start_callback(worker); +} + +void Server::worker_stop_callback(Worker *worker) { + call_worker_stop_callback(worker); } -void Server::worker_stop_callback() { +void Server::call_worker_start_callback(Worker *worker) { void *hook_args[2]; hook_args[0] = this; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; - if (SwooleG.hooks[SW_GLOBAL_HOOK_BEFORE_WORKER_STOP]) { + hook_args[1] = (void *) (uintptr_t) worker->id; + + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args); + } + if (isset_hook(HOOK_WORKER_START)) { + call_hook(Server::HOOK_WORKER_START, hook_args); + } + + swoole_clear_last_error(); + swoole_clear_last_error_msg(); + + if (onWorkerStart) { + onWorkerStart(this, worker); + } +} + +void Server::call_worker_stop_callback(Worker *worker) { + void *hook_args[2]; + hook_args[0] = this; + hook_args[1] = (void *) (uintptr_t) worker->id; + + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } if (onWorkerStop) { - onWorkerStop(this, SwooleG.process_id); + onWorkerStop(this, worker); } - if (!message_bus.empty()) { + + if (!get_worker_message_bus()->empty()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); - message_bus.clear(); + get_worker_message_bus()->clear(); + } + + SwooleWG.running = false; + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; } } +void Server::call_worker_error_callback(Worker *worker, const ExitStatus &status) { + if (onWorkerError != nullptr) { + onWorkerError(this, worker, status); + } + /** + * The work process has exited unexpectedly, requiring a cleanup of the shared memory state. + * This must be done between the termination of the old process and the initiation of the new one; + * otherwise, data contention may occur. + */ + if (worker->type == SW_PROCESS_EVENTWORKER) { + abort_worker(worker); + } +} + +bool Server::kill_worker(WorkerId worker_id, bool wait_reactor) { + if (is_thread_mode()) { + DataHead event = {}; + event.type = SW_SERVER_EVENT_SHUTDOWN; + return send_to_worker_from_worker(get_worker(worker_id), &event, sizeof(event), SW_PIPE_MASTER) != -1; + } + + if (worker_id == sw_worker()->id && !wait_reactor) { + if (swoole_event_is_available()) { + swoole_event_defer([](void *data) { sw_reactor()->running = false; }, nullptr); + } + running = false; + } else { + Worker *worker = get_worker(worker_id); + if (worker == nullptr) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "the worker_id[%d] is invalid", worker_id); + return false; + } + if (swoole_kill(worker->pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d, SIGTERM) failed", worker->pid); + return false; + } + } + return true; +} + void Server::stop_async_worker(Worker *worker) { - worker->status = SW_WORKER_EXIT; Reactor *reactor = SwooleTG.reactor; + worker->shutdown(); + if (worker->type == SW_PROCESS_EVENTWORKER) { + reset_worker_counter(worker); + } + /** * force to end. */ if (reload_async == 0) { - running = false; reactor->running = false; return; } @@ -419,16 +397,10 @@ void Server::stop_async_worker(Worker *worker) { } // Separated from the event worker process pool - worker = (Worker *) sw_malloc(sizeof(*worker)); - *worker = *SwooleWG.worker; + SwooleWG.worker_copy = new Worker{}; + *SwooleWG.worker_copy = *worker; SwooleWG.worker = worker; - if (stream_socket) { - reactor->del(stream_socket); - stream_socket->free(); - stream_socket = nullptr; - } - if (worker->pipe_worker && !worker->pipe_worker->removed) { reactor->remove_read_event(worker->pipe_worker); } @@ -456,14 +428,22 @@ void Server::stop_async_worker(Worker *worker) { }); clear_timer(); } - } else { + } else if (is_process_mode()) { WorkerStopMessage msg; msg.pid = SwooleG.pid; - msg.worker_id = SwooleG.process_id; + msg.worker_id = worker->id; if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { - running = 0; + swoole_sys_warning("failed to push WORKER_STOP message"); } + } else if (is_thread_mode()) { + foreach_connection([this, reactor](Connection *conn) { + if (conn->reactor_id == reactor->id && !conn->peer_closed && !conn->socket->removed) { + reactor->remove_read_event(conn->socket); + } + }); + } else { + assert(0); } reactor->set_wait_exit(true); @@ -471,14 +451,11 @@ void Server::stop_async_worker(Worker *worker) { SwooleWG.exit_time = ::time(nullptr); Worker_reactor_try_to_exit(reactor); - if (!reactor->running) { - running = false; - } } static void Worker_reactor_try_to_exit(Reactor *reactor) { Server *serv; - if (SwooleG.process_type == SW_PROCESS_TASKWORKER) { + if (swoole_get_process_type() == SW_PROCESS_TASKWORKER) { ProcessPool *pool = (ProcessPool *) reactor->ptr; serv = (Server *) pool->ptr; } else { @@ -489,10 +466,9 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { while (1) { if (reactor->if_exit()) { reactor->running = false; - break; } else { if (serv->onWorkerExit && call_worker_exit_func == 0) { - serv->onWorkerExit(serv, SwooleG.process_id); + serv->onWorkerExit(serv, sw_worker()); call_worker_exit_func = 1; continue; } @@ -501,7 +477,6 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced termination"); reactor->running = false; - break; } else { int timeout_msec = remaining_time * 1000; if (reactor->timeout_msec < 0 || reactor->timeout_msec > timeout_msec) { @@ -527,19 +502,37 @@ void Server::drain_worker_pipe() { } } +void Server::clean_worker_connections(Worker *worker) { + sw_reactor()->destroyed = true; + + if (is_thread_mode()) { + foreach_connection([this, worker](Connection *conn) { + if (conn->reactor_id == worker->id) { + close(conn->session_id, true); + } + }); + } else if (is_base_mode()) { + foreach_connection([this](Connection *conn) { close(conn->session_id, true); }); + } else { + return; + } +} + /** * main loop [Worker] */ int Server::start_event_worker(Worker *worker) { - // worker_id - SwooleG.process_id = worker->id; + swoole_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); - init_worker(worker); + init_event_worker(worker); if (swoole_event_init(0) < 0) { return SW_ERR; } + worker_signal_init(); + Reactor *reactor = SwooleTG.reactor; /** * set pipe buffer size @@ -559,22 +552,12 @@ int Server::start_event_worker(Worker *worker) { reactor->add(worker->pipe_worker, SW_EVENT_READ); reactor->set_handler(SW_FD_PIPE, Worker_onPipeReceive); - if (dispatch_mode == DISPATCH_STREAM) { - reactor->add(stream_socket, SW_EVENT_READ); - reactor->set_handler(SW_FD_STREAM_SERVER, Worker_onStreamAccept); - reactor->set_handler(SW_FD_STREAM, Worker_onStreamRead); - network::Stream::set_protocol(&stream_protocol); - stream_protocol.private_data_2 = this; - stream_protocol.package_max_length = UINT_MAX; - stream_protocol.onPackage = Worker_onStreamPackage; - buffer_pool = new std::queue; - } else if (dispatch_mode == DISPATCH_CO_CONN_LB || dispatch_mode == DISPATCH_CO_REQ_LB) { + if (dispatch_mode == DISPATCH_CO_CONN_LB || dispatch_mode == DISPATCH_CO_REQ_LB) { reactor->set_end_callback(Reactor::PRIORITY_WORKER_CALLBACK, [worker](Reactor *) { worker->coroutine_num = Coroutine::count(); }); } - worker->status = SW_WORKER_IDLE; - worker_start_callback(); + worker_start_callback(worker); // main loop reactor->wait(nullptr); @@ -583,7 +566,7 @@ int Server::start_event_worker(Worker *worker) { // reactor free swoole_event_free(); // worker shutdown - worker_stop_callback(); + worker_stop_callback(worker); if (buffer_pool) { delete buffer_pool; @@ -616,44 +599,16 @@ ssize_t Server::send_to_worker_from_worker(Worker *dst_worker, const void *buf, */ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - PipeBuffer *pipe_buffer = serv->message_bus.get_buffer(); + PipeBuffer *pipe_buffer = serv->get_worker_message_bus()->get_buffer(); - if (serv->message_bus.read(event->socket) <= 0) { + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { return SW_OK; } serv->worker_accept_event(&pipe_buffer->info); - serv->message_bus.pop(); + serv->get_worker_message_bus()->pop(); return SW_OK; } -ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { - Socket *pipe_sock; - - if (flags & SW_PIPE_MASTER) { - pipe_sock = pipe_master; - } else { - pipe_sock = pipe_worker; - } - - // message-queue - if (pool->use_msgqueue) { - struct { - long mtype; - EventData buf; - } msg; - - msg.mtype = id + 1; - memcpy(&msg.buf, buf, n); - - return pool->queue->push((QueueNode *) &msg, n) ? n : -1; - } - - if ((flags & SW_PIPE_NONBLOCK) && swoole_event_is_available()) { - return swoole_event_write(pipe_sock, buf, n); - } else { - return pipe_sock->send_blocking(buf, n); - } -} } // namespace swoole diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index 52be0141548..8598a4bcca6 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -85,7 +85,7 @@ int swoole_event_wait() { Reactor *reactor = SwooleTG.reactor; int retval = 0; if (!reactor->wait_exit or !reactor->if_exit()) { - retval = SwooleTG.reactor->wait(nullptr); + retval = reactor->wait(nullptr); } swoole_event_free(); return retval; @@ -124,6 +124,10 @@ bool swoole_event_is_available() { return SwooleTG.reactor and !SwooleTG.reactor->destroyed; } +bool swoole_event_is_running() { + return SwooleTG.reactor and SwooleTG.reactor->running; +} + Socket *swoole_event_get_socket(int fd) { return SwooleTG.reactor->get_socket(fd); } diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc new file mode 100644 index 00000000000..214ecbcbb57 --- /dev/null +++ b/src/wrapper/http.cc @@ -0,0 +1,364 @@ +/** + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "swoole_api.h" +#include "swoole_http.h" +#include "swoole_server.h" + +#include "thirdparty/swoole_http_parser.h" +#include "thirdparty/multipart_parser.h" + +namespace swoole { +namespace http_server { + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +static int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_header_complete(multipart_parser *p); +static int multipart_body_on_data_end(multipart_parser *p); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; + +static const multipart_parser_settings mt_parser_settings = +{ + multipart_body_on_header_field, + multipart_body_on_header_value, + multipart_body_on_data, + nullptr, + multipart_body_on_header_complete, + multipart_body_on_data_end, + nullptr, +}; +// clang-format on + +struct ContextImpl { + swoole_http_parser parser; + multipart_parser *mt_parser; + + std::string current_header_name; + std::string current_input_name; + std::string current_form_data_name; + String *form_data_buffer; + + bool is_beginning = true; + + bool parse(Context &ctx, const char *at, size_t length) { + parser.data = &ctx; + swoole_http_parser_init(&parser, PHP_HTTP_REQUEST); + swoole_http_parser_execute(&parser, &http_parser_settings, at, length); + return true; + } +}; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->request_path = std::string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->impl->current_header_name = std::string(at, length); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ContextImpl *impl = ctx->impl; + ctx->headers[impl->current_header_name] = std::string(at, length); + + if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || + parser->method == PHP_HTTP_PATCH) && + SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), "content-type")) { + if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) { + ctx->post_form_urlencoded = 1; + } else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) { + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!parse_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) { + return -1; + } + impl->mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + impl->form_data_buffer = new String(SW_BUFFER_SIZE_STD); + impl->mt_parser->data = ctx; + swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); + } + } + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->query_string = std::string(at, length); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + Context *ctx = (Context *) parser->data; + ctx->version = parser->http_major * 100 + parser->http_minor; + ctx->server_protocol = std::string(ctx->version == 101 ? "HTTP/1.1" : "HTTP/1.0"); + ctx->keepalive = swoole_http_should_keep_alive(parser); + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + if (length == 0) { + return 0; + } + + Context *ctx = (Context *) parser->data; + ContextImpl *impl = ctx->impl; + + if (impl->mt_parser != nullptr) { + multipart_parser *multipart_parser = impl->mt_parser; + if (impl->is_beginning) { + /* Compatibility: some clients may send extra EOL */ + do { + if (*at != '\r' && *at != '\n') { + break; + } + at++; + length--; + } while (length != 0); + impl->is_beginning = false; + } + size_t n = multipart_parser_execute(multipart_parser, at, length); + if (n != length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + length); + } + } else { + ctx->body.append(at, length); + } + + return 0; +} + +static int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + return http_request_on_header_field(&impl->parser, at, length); +} + +static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + const char *header_name = impl->current_header_name.c_str(); + size_t header_len = impl->current_header_name.length(); + + if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { + std::unordered_map info; + ParseCookieCallback cb = [&info](char *key, size_t key_len, char *value, size_t value_len) { + info[std::string(key, key_len)] = std::string(value, value_len); + return true; + }; + swoole::http_server::parse_cookie(at, length, cb); + auto name = info.find("name"); + auto filename = info.find("filename"); + if (filename == info.end()) { + impl->current_form_data_name = name->second; + } else { + impl->current_input_name = filename->second; + } + } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) { + ctx->files[impl->current_form_data_name] = std::string(at, length); + } + + return 0; +} + +static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + if (!impl->current_form_data_name.empty()) { + impl->form_data_buffer->append(at, length); + return 0; + } + if (p->fp == nullptr) { + return 0; + } + ssize_t n = fwrite(at, sizeof(char), length, p->fp); + if (n != (off_t) length) { + ctx->files[impl->current_form_data_name] = "ERROR(1)"; + fclose(p->fp); + p->fp = nullptr; + swoole_sys_warning("write upload file failed"); + } + return 0; +} + +static int multipart_body_on_header_complete(multipart_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + if (impl->current_input_name.empty()) { + return 0; + } + + if (ctx->files.find(impl->current_form_data_name) != ctx->files.end()) { + return 0; + } + + char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE] = "/tmp/swoole.upfile.XXXXXX"; + int tmpfile = swoole_tmpfile(file_path); + if (tmpfile < 0) { + return 0; + } + + FILE *fp = fdopen(tmpfile, "wb+"); + if (fp == nullptr) { + swoole_sys_warning("fopen(%s) failed", file_path); + return 0; + } + p->fp = fp; + ctx->files[impl->current_form_data_name] = file_path; + + return 0; +} + +static int multipart_body_on_data_end(multipart_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + + if (!impl->current_form_data_name.empty()) { + ctx->form_data[impl->current_form_data_name] = impl->form_data_buffer->to_std_string(); + impl->form_data_buffer->clear(); + } + + if (p->fp != nullptr) { + fclose(p->fp); + p->fp = nullptr; + } + + impl->current_header_name.clear(); + impl->current_input_name.clear(); + impl->current_form_data_name.clear(); + + return 0; +} + +static int http_request_message_complete(swoole_http_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + + if (impl->form_data_buffer) { + delete impl->form_data_buffer; + impl->form_data_buffer = nullptr; + } + + return 1; +} + +bool Context::end(const char *data, size_t length) { + char buf[1024]; + sw_tg_buffer()->clear(); + sw_tg_buffer()->append(SW_STRL("HTTP/1.1 ")); + sw_tg_buffer()->append(get_status_message(response.code)); + sw_tg_buffer()->append(SW_STRL("\r\n")); + if (length > 0) { + response.headers["Content-Length"] = std::to_string(length); + } + for (auto &iter : response.headers) { + size_t n = sw_snprintf(buf, sizeof(buf), "%s: %s\r\n", iter.first.c_str(), iter.second.c_str()); + sw_tg_buffer()->append(buf, n); + } + if (!server_->send(session_id_, sw_tg_buffer()->str, sw_tg_buffer()->length)) { + swoole_warning("failed to send HTTP header"); + return false; + } + if (length > 0 && !server_->send(session_id_, data, length)) { + swoole_warning("failed to send HTTP body"); + return false; + } + return true; +} + +Context::~Context() { + for (auto &kv : files) { + if (file_exists(kv.second)) { + unlink(kv.second.c_str()); + } + } +} + +std::shared_ptr listen(const std::string addr, std::function cb, int mode) { + std::shared_ptr server = std::make_shared((Server::Mode) mode); + auto index = addr.find(':'); + if (index == addr.npos) { + swoole_warning("incorrect server listening address"); + return nullptr; + } + + std::string host = addr.substr(0, index); + if (host.empty()) { + host = "0.0.0.0"; + } + + int port = atoi(addr.substr(index + 1).c_str()); + auto port_object = server->add_port(SW_SOCK_TCP, host.c_str(), port); + if (!port_object) { + return nullptr; + } + + server->onReceive = [&cb](Server *server, RecvData *req) { + SessionId session_id = req->info.fd; + Connection *conn = server->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); + return SW_OK; + } + ContextImpl impl; + Context ctx(server, session_id, &impl); + if (impl.parse(ctx, req->data, req->info.len)) { + cb(ctx); + } + return SW_OK; + }; + + port_object->open_http_protocol = true; + + if (server->create() == SW_ERR) { + return nullptr; + } + + return server; +} +} // namespace http_server +} // namespace swoole diff --git a/src/wrapper/timer.cc b/src/wrapper/timer.cc index dad46c3e354..6cfb31e4ef9 100644 --- a/src/wrapper/timer.cc +++ b/src/wrapper/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -29,6 +29,14 @@ bool swoole_timer_is_available() { return SwooleTG.timer != nullptr; } +TimerNode *swoole_timer_add(double timeout, bool persistent, const TimerCallback &callback, void *private_data) { + if (timeout < SW_TIMER_MIN_SEC) { + return swoole_timer_add(1L, persistent, callback, private_data); + } + + return swoole_timer_add((long) (timeout * 1000), persistent, callback, private_data); +} + TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) { if (sw_unlikely(!swoole_timer_is_available())) { SwooleTG.timer = new Timer(); @@ -115,12 +123,10 @@ void swoole_timer_free() { } delete SwooleTG.timer; SwooleTG.timer = nullptr; - SwooleG.signal_alarm = false; } int swoole_timer_select() { if (!swoole_timer_is_available()) { - swoole_warning("timer is not available"); return SW_ERR; } return SwooleTG.timer->select(); diff --git a/tests/.php-cs-fixer.dist.php b/tests/.php-cs-fixer.dist.php new file mode 100644 index 00000000000..86a88780b9f --- /dev/null +++ b/tests/.php-cs-fixer.dist.php @@ -0,0 +1,95 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + '@Symfony' => true, + '@DoctrineAnnotation' => true, + '@PhpCsFixer' => true, + 'header_comment' => [ + 'comment_type' => 'PHPDoc', + 'header' => $header, + 'separate' => 'bottom', + 'location' => 'after_open', + ], + 'array_syntax' => [ + 'syntax' => 'short' + ], + 'list_syntax' => [ + 'syntax' => 'short' + ], + 'concat_space' => [ + 'spacing' => 'one' + ], + 'blank_line_before_statement' => [ + 'statements' => [ + 'declare', + ], + ], + 'blank_line_after_namespace' => true, + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author' + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'single_line_comment_style' => [ + 'comment_types' => [ + ], + ], + 'yoda_style' => [ + 'always_move_variable' => false, + 'equal' => false, + 'identical' => false, + ], + 'phpdoc_align' => [ + 'align' => 'left', + ], + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'constant_case' => [ + 'case' => 'lower', + ], + 'class_attributes_separation' => true, + 'combine_consecutive_unsets' => true, + 'declare_strict_types' => true, + 'linebreak_after_opening_tag' => true, + 'lowercase_static_reference' => true, + 'no_useless_else' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => false, + 'not_operator_with_space' => false, + 'ordered_class_elements' => true, + 'php_unit_strict' => false, + 'phpdoc_separation' => false, + 'phpdoc_summary' => false, + 'single_quote' => true, + 'increment_style' => [], + 'standardize_increment' => false, + 'standardize_not_equals' => true, + 'multiline_comment_opening_closing' => true, + 'lambda_not_used_import' => false, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude(['html', 'vendor']) + ->in(__DIR__) + ) + ->setUsingCache(false); diff --git a/tests/README.md b/tests/README.md index 9cb4e9ec673..fc3ce7a8263 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,7 +8,7 @@ try to run `./init` to initialize the databases. | | mysql | redis | | ------------- | ------------------------------------- | ----------------------------------- | | path (env) | $MYSQL_SERVER_PATH | $REDIS_SERVER_PATH | -| path (travis) | ${travis}/data/run/mysqld/mysqld.sock | ${travis}/data/run/redis/redis.sock | +| path (actions) | ${actions}/data/run/mysqld/mysqld.sock | ${actions}/data/run/redis/redis.sock | | host (raw) | 127.0.0.1 | 127.0.0.1 | | host (docker) | mysql | redis | | port | 3306 | 6379 | diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index 9e034e0a613..30d457e8d8c 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -1,14 +1,20 @@ = 80400) { + $exit_status_list = [ + 1, + 'exit', + 0, + ]; +} else { + $exit_status_list = [ + 'undef', + null, + true, + false, + 1, + 1.1, + 'exit', + ['exit' => 'ok'], + (object)['exit' => 'ok'], + STDIN, + 0 + ]; +} + +function route() +{ + controller(); +} + +function controller() +{ + your_code(); +} + +function your_code() +{ + global $exit_status_list; + co::sleep(.001); + $exit_status = array_shift($exit_status_list); + if ($exit_status === 'undef') { + exit; + } else { + exit($exit_status); + } +} + +$chan = new Swoole\Coroutine\Channel; + +go(function () use ($chan, $exit_status_list) { + foreach ($exit_status_list as $val) { + $chan->push($val); + } +}); + +for ($i = 0; $i < count($exit_status_list); $i++) { + go(function () use ($exit_status_list, $chan) { + try { + // in coroutine + route(); + } catch (\Swoole\ExitException $e) { + Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE); + $exit_status = $chan->pop(); + if (PHP_VERSION_ID < 80400) { + $exit_status = $exit_status === 'undef' ? null : $exit_status; + } + Assert::same($e->getStatus(), $exit_status); + var_dump($e->getStatus()); + // exit coroutine + return; + } + echo "never here\n"; + }); +} + +Swoole\Event::wait(); diff --git a/tests/include/api/http_server.php b/tests/include/api/http_server.php index b25237eff6f..40211a89e10 100644 --- a/tests/include/api/http_server.php +++ b/tests/include/api/http_server.php @@ -4,87 +4,49 @@ */ global $pm; -$http = new swoole_http_server("127.0.0.1", $pm->getFreePort(), SWOOLE_BASE); +$http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null', "http_parse_post" => 1, "upload_tmp_dir" => "/tmp", )); -$http->on("WorkerStart", function (\swoole_server $serv) -{ - /** - * @var $pm ProcessManager - */ +$http->on("WorkerStart", function (Swoole\Server $serv) { global $pm; - if ($pm) - { + if ($pm) { $pm->wakeup(); } }); -$http->on('request', function ($request, swoole_http_response $response) use ($pm) -{ +$http->on('request', function ($request, Swoole\Http\Response $response) use ($pm) { $route = $request->server['request_uri']; - if ($route == '/info') - { + if ($route == '/info') { $response->end($request->header['user-agent']); return; - } - elseif ($route == '/cookies') - { + } elseif ($route == '/cookies') { $response->end(@json_encode($request->cookie)); return; - } - elseif ($route == '/get') - { + } elseif ($route == '/get') { $response->end(@json_encode($request->get)); return; - } - elseif ($route == '/post') - { + } elseif ($route == '/post') { $response->end(@json_encode($request->post)); return; - } - elseif ($route == '/get_file') - { + } elseif ($route == '/get_file') { $response->sendfile(TEST_IMAGE); return; - } - elseif ($route == '/upload_file') - { + } elseif ($route == '/upload_file') { $response->end(json_encode([ 'files' => $request->files, 'md5' => md5_file($request->files['test_jpg']['tmp_name']), 'post' => $request->post ])); return; - } - elseif ($route == '/gzip') - { + } elseif ($route == '/gzip') { $response->gzip(5); $content = co::readFile(__DIR__ . '/../../../README.md'); $response->end($content); return; - } - else - { - $cli = new swoole_http_client('127.0.0.1', $pm->getFreePort()); - $cli->set(array( - 'timeout' => 0.3, - )); - $cli->setHeaders(array('User-Agent' => "swoole")); - $cli->on('close', function ($cli) use ($response) - { - }); - $cli->on('error', function ($cli) use ($response) - { - echo "error"; - $response->end("error"); - }); - $cli->get('/info', function ($cli) use ($response) - { - $response->end($cli->body . "\n"); - $cli->close(); - }); + } else { + return; } }); -$http->start(); \ No newline at end of file +$http->start(); diff --git a/tests/include/api/http_test_cases.php b/tests/include/api/http_test_cases.php new file mode 100644 index 00000000000..cf6819d8788 --- /dev/null +++ b/tests/include/api/http_test_cases.php @@ -0,0 +1,297 @@ +getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/html?bytes=' . rand(8192, 65536)); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new HttpClient('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/json?bytes=' . rand(8192, 65536)); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new HttpClient('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/raw?bytes=' . rand(8192, 65536)); + Assert::assert(!isset($cli->getHeaders()['content-encoding'])); + }); +} + +function http2_compression_types_test(ProcessManager $pm) +{ + run(function () use ($pm) { + $cli = new Http2Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + + $req1 = new Http2Request; + $req1->path = '/html?bytes=' . rand(8192, 65536); + $req1->headers = ['Accept-Encoding' => 'gzip',]; + $stream1 = $cli->send($req1); + + $req2 = new Http2Request; + $req2->path = '/json?bytes=' . rand(8192, 65536); + $req2->headers = ['Accept-Encoding' => 'gzip',]; + $stream2 = $cli->send($req2); + + $req3 = new Http2Request; + $req3->path = '/raw?bytes=' . rand(8192, 65536); + $req3->headers = ['Accept-Encoding' => 'gzip',]; + $stream3 = $cli->send($req3); + + $n = 3; + $list = []; + while($n--) { + $resp = $cli->recv(); + $list[$resp->streamId] = $resp; + } + + Assert::eq($list[$stream1]->headers['content-encoding'], 'gzip'); + Assert::eq($list[$stream2]->headers['content-encoding'], 'gzip'); + Assert::assert(!isset($list[$stream3]->headers['content-encoding'])); + }); +} + +/** + * @param ProcessManager $pm + * @param array $sizes + * @throw RuntimeException + */ +function form_data_test(ProcessManager $pm, array $sizes = []) +{ + if (count($sizes) == 0) { + throw new \RuntimeException("size array cannot be empty"); + } + run(function () use ($pm, $sizes) { + $client = new Client(SWOOLE_SOCK_TCP); + Assert::true($client->connect('127.0.0.1', $pm->getFreePort())); + $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); + $len = strlen($req); + + $begin = 0; + foreach ($sizes as $end) { + if ($end >= $len) { + throw new \RuntimeException("error offset[$end]"); + } + Assert::eq($client->send(substr($req, $begin, $end)), $end - $begin); + usleep(10000); + } + + $end = $sizes[count($sizes) - 1]; + if ($len - $end > 0) { + Assert::eq($client->send(substr($req, $end)), $len - $end); + usleep(10000); + } + + $resp = ''; + $length = 0; + $header = ''; + + while (true) { + $data = $client->recv(); + if ($data == false) { + throw new RuntimeException("recv failed, error: " . $client->errMsg . ", resp: " . $resp); + } + $resp .= $data; + if ($length == 0) { + $crlf_pos = strpos($resp, "\r\n\r\n"); + if ($crlf_pos === false) { + continue; + } + $header = substr($resp, 0, $crlf_pos); + if (!preg_match('#Content-Length:\s(\d+)#i', $header, $match)) { + throw new RuntimeException("no match Content-Length"); + } + $length = strlen($header) + 4 + $match[1]; + } + if (strlen($resp) == $length) { + break; + } + } + Assert::assert($header); + $body = substr($resp, strlen($header) + 4); + if (!$body) { + var_dump($header); + } + Assert::assert($body); + $data = json_decode($body); + Assert::assert(is_object($data)); + Assert::minLength($data->test, 80); + Assert::minLength($data->hello, 120); + Assert::minLength($data->world, 1024); + $client->close(); + }); + $pm->kill(); + echo "DONE\n"; +} + +function getHttpBody(string $s): string +{ + return str_replace(EOF, '', explode("\r\n\r\n", $s)[1] ?? ''); +} + +function generateChunkBody(array $a): string +{ + $s = ''; + foreach ($a as $c) { + $s .= dechex(strlen($c)) . "\r\n" . $c . "\r\n"; + } + return $s . "0\r\n"; +} + +function chunked_request(ProcessManager $pm) +{ + run(function () use ($pm) { + $request_empty_chunked = + "DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\r\n" . + "x-real-ip: 10.2.100.1\r\n" . + "x-forwarded-server: kitchen.pool-x.net\r\n" . + "accept: application/json\r\n" . + "origin: http://pool-x.net\r\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" . + "sec-fetch-site: cross-site\r\n" . + "sec-fetch-mode: cors\r\n" . + "referer: http://pool-x.net/assets/staking\r\n" . + "accept-encoding: gzip, deflate, br\r\n" . + "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\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" . + "x-domain: kitchen.pool-x.net\r\n" . + "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . + "x-device-id: \r\n" . + "x-origin-domain-id: pool\r\n" . + "x-forwarded-proto: http\r\n" . + "uber-trace-id: ffaf3497a6deee40%3A8afa1564e1e0783f%3Affaf3497a6deee40%3A1\r\n" . + "x-forwarded-port: 80\r\n" . + "x-forwarded-for: 127.0.0.1\r\n" . + "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . + "x-domain-id: pool\r\n" . + "kyc-country: \r\n" . + "kyc-status: \r\n" . + "x-forwarded-host: kitchen.pool-x.net\r\n" . + "x-forwarded-prefix: /pool-staking\r\n" . + "gateway-type: WEB\r\n" . + "lang: zh_CN\r\n" . + "Transfer-Encoding: chunked\r\n" . + "Host: 10.2.1.51:9526\r\n" . + "Connection: Keep-Alive\r\n" . + "\r\n" . + "0\r\n" . + "\r\n"; + $request_zero_length = + "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" . + "x-real-ip: 10.2.100.1\r\n" . + "x-forwarded-server: kitchen.pool-x.net\r\n" . + "accept: application/json\r\n" . + "origin: http://pool-x.net\r\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" . + "sec-fetch-site: cross-site\r\n" . + "sec-fetch-mode: cors\r\n" . + "referer: http://pool-x.net/assets/staking\r\n" . + "accept-encoding: gzip, deflate, br\r\n" . + "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\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" . + "x-domain: kitchen.pool-x.net\r\n" . + "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . + "x-device-id: \r\n" . + "x-origin-domain-id: pool\r\n" . + "x-forwarded-proto: http\r\n" . + "uber-trace-id: df854c374e6d4fde%3Ada6b1dc2e4e112b5%3Adf854c374e6d4fde%3A0\r\n" . + "x-forwarded-port: 80\r\n" . + "x-forwarded-for: 127.0.0.1\r\n" . + "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . + "x-domain-id: pool\r\n" . + "kyc-country: \r\n" . + "kyc-status: \r\n" . + "x-forwarded-host: kitchen.pool-x.net\r\n" . + "x-forwarded-prefix: /pool-staking\r\n" . + "gateway-type: WEB\r\n" . + "lang: zh_CN\r\n" . + "Content-Length: 0\r\n" . + "Host: 10.2.1.51:9526\r\n" . + "Connection: Keep-Alive\r\n" . + "\r\n"; + $request_chunked_body_array = ['FOO', 'BAR', 'CHAR', str_repeat('Z', mt_rand(10, 1000))]; + $request_chunked_body = generateChunkBody($request_chunked_body_array); + $request_chunked = "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n{$request_chunked_body}\r\n"; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::true($socket->connect('127.0.0.1', $pm->getFreePort())); + Assert::true($socket->setProtocol([ + 'open_eof_check' => true, + 'package_eof' => EOF + ])); + /* chunked */ + $ret = $socket->sendAll($request_empty_chunked); + Assert::same($ret, strlen($request_empty_chunked)); + $ret = $socket->recvPacket(); + Assert::isEmpty(getHttpBody($ret)); + /* pipeline */ + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->sendAll($request_zero_length); + Assert::same($ret, strlen($request_zero_length)); + } + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->recvPacket(); + Assert::same(getHttpBody($ret), getHttpBody($request_zero_length)); + } + /* chunked */ + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->sendAll($request_chunked); + Assert::same($ret, strlen($request_chunked)); + $ret = $socket->recvPacket(); + Assert::same(getHttpBody($ret), implode('', $request_chunked_body_array)); + } + }); + echo "SUCCESS\n"; + $pm->kill(); +} + +function http_get_with_co_socket(string $domain, ?callable $cb = null) +{ + $cli = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + $cli->setProtocol(['open_ssl' => true,]); + + if (!$cli->connect($domain, 443)) { + echo "ERROR\n"; + } + + $http = "GET / HTTP/1.1\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: $domain\r\nConnection: Keep-Alive\r\n" + . "Keep-Alive: on\r\n\r\n"; + if (!$cli->send($http)) { + echo "ERROR\n"; + } + + $content = ''; + $length = 0; + while (true) { + $read = $cli->recv(); + if (empty($read)) { + var_dump($read); + break; + } + $content .= $read; + if ($length == 0) { + if (preg_match('#Content-Length: (\d+)#i', $content, $match)) { + $length = intval($match[1]); + } + } + $header_length = strpos($content, "\r\n\r\n"); + if (strlen($content) == $length + $header_length + 4) { + break; + } + } + + if ($cb) { + $cb($cli, $content); + } + $cli->close(); + return $content; +} diff --git a/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php b/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php index 6eb9b80a81e..0c8c6208da5 100644 --- a/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php +++ b/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php @@ -21,7 +21,7 @@ function send($str, $onRecv) static $client; if ($client === null) { - $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $client->on("error", function($cli) { echo "error"; }); $client->on("close", function($cli) { echo "close"; }); diff --git a/tests/include/api/swoole_client/connect_timeout.php b/tests/include/api/swoole_client/connect_timeout.php index f713018e40b..fb0c064e3b5 100644 --- a/tests/include/api/swoole_client/connect_timeout.php +++ b/tests/include/api/swoole_client/connect_timeout.php @@ -1,11 +1,11 @@ on("connect", function(swoole_client $cli) { +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli->on("connect", function(Swoole\Client $cli) { Assert::true(false, 'never here'); }); -$cli->on("receive", function(swoole_client $cli, $data) { +$cli->on("receive", function(Swoole\Client $cli, $data) { Assert::true(false, 'never here'); }); -$cli->on("error", function(swoole_client $cli) { echo "connect timeout\n"; }); -$cli->on("close", function(swoole_client $cli) { echo "close\n"; }); -$cli->connect("11.11.11.11", 9000, 0.5); \ No newline at end of file +$cli->on("error", function(Swoole\Client $cli) { echo "connect timeout\n"; }); +$cli->on("close", function(Swoole\Client $cli) { echo "close\n"; }); +$cli->connect("11.11.11.11", 9000, 0.5); diff --git a/tests/include/api/swoole_client/connect_twice.php b/tests/include/api/swoole_client/connect_twice.php index efe5cfb3042..7aa951c0319 100644 --- a/tests/include/api/swoole_client/connect_twice.php +++ b/tests/include/api/swoole_client/connect_twice.php @@ -2,17 +2,17 @@ $start = microtime(true); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); -$cli->on("connect", function(swoole_client $cli) { +$cli = new Swoole\Async\Client(SWOOLE_SOCK_TCP); +$cli->on("connect", function(Swoole\Async\Client $cli) { Assert::true(false, 'never here'); }); -$cli->on("receive", function(swoole_client $cli, $data) { +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { Assert::true(false, 'never here'); }); -$cli->on("error", function(swoole_client $cli) { +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); -$cli->on("close", function(swoole_client $cli) { +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); @@ -31,4 +31,4 @@ function refcount($var) @$cli->connect("11.11.11.11", 9000, 0.1); Swoole\Event::wait(); // xdebug_debug_zval("cli"); -// echo refcount($cli); // php7无效 \ No newline at end of file +// echo refcount($cli); // php7无效 diff --git a/tests/include/api/swoole_client/http_get.php b/tests/include/api/swoole_client/http_get.php new file mode 100644 index 00000000000..da81bdf1130 --- /dev/null +++ b/tests/include/api/swoole_client/http_get.php @@ -0,0 +1,21 @@ +connect('httpbin.org', 80, 10)); + Assert::assert($client->send("GET / HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n")); + + $resp = ''; + while (true) { + $data = $client->recv(); + if ($data === '' || $data === false) { + break; + } + $resp .= $data; + } + + Assert::assert(str_starts_with($resp, 'HTTP/1.1 200 OK')); + Assert::assert(str_contains($resp, 'https://github.com/requests/httpbin')); +} diff --git a/tests/include/api/swoole_client/opcode_client.php b/tests/include/api/swoole_client/opcode_client.php index 589415f02c9..d896fda03ec 100644 --- a/tests/include/api/swoole_client/opcode_client.php +++ b/tests/include/api/swoole_client/opcode_client.php @@ -7,7 +7,7 @@ // suicide(5000); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ @@ -17,31 +17,31 @@ 'package_body_offset' => 0, ])); -$cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); Assert::true($cli->isConnected()); }); -$cli->on("receive", function(swoole_client $cli, $data){ +$cli->on("receive", function(Swoole\Client $cli, $data){ $cli->close(); Assert::false($cli->isConnected()); }); -$cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); echo "ERROR"; }); -$cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); echo "CLOSE"; }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); -$cli->timeo_id = swoole_timer_after(1000, function() use($cli) { +$cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); diff --git a/tests/include/api/swoole_client/simple_client.php b/tests/include/api/swoole_client/simple_client.php index 54b5eb0ca33..f3fc6e610cb 100644 --- a/tests/include/api/swoole_client/simple_client.php +++ b/tests/include/api/swoole_client/simple_client.php @@ -8,7 +8,7 @@ suicide(5000); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ @@ -20,8 +20,8 @@ // "socket_buffer_size" => 1, ])); -$cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); // TODO getSocket BUG // assert(is_resource($cli->getSocket())); @@ -37,7 +37,7 @@ // $cli->sendfile(__DIR__.'/test.txt'); }); -$cli->on("receive", function(swoole_client $cli, $data){ +$cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(RandStr::gen(1024, RandStr::ALL)); @@ -45,18 +45,18 @@ Assert::false($cli->isConnected()); }); -$cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); -$cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); -$cli->timeo_id = swoole_timer_after(1000, function() use($cli) { +$cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); diff --git a/tests/include/api/swoole_client/socket_free.php b/tests/include/api/swoole_client/socket_free.php index da3c48e59d2..0944ee1b73f 100644 --- a/tests/include/api/swoole_client/socket_free.php +++ b/tests/include/api/swoole_client/socket_free.php @@ -2,12 +2,12 @@ // swoole socket 复用BUG -function onClose(swoole_client $cli) { +function onClose(Swoole\Client $cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "close fd <$fd>\n"; } -function onError(swoole_client $cli) { +function onError(Swoole\Client $cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "error fd <$fd>\n"; } @@ -15,23 +15,23 @@ function onError(swoole_client $cli) { $host = "127.0.0.1"; $port = 8050; -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); -$cli->on("receive", function(swoole_client $cli, $data){ }); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli->on("receive", function(Swoole\Client $cli, $data){ }); $cli->on("error", "onError"); $cli->on("close", "onClose"); -$cli->on("connect", function(swoole_client $cli) use($host, $port) { +$cli->on("connect", function(Swoole\Client $cli) use($host, $port) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "connected fd <$fd>\n"; $cli->close(); // close(fd) // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - $newCli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $newCli->on("receive", function(swoole_client $cli, $data){ }); + $newCli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $newCli->on("receive", function(Swoole\Client $cli, $data){ }); $newCli->on("error", "onError"); $newCli->on("close", "onClose"); - $newCli->on("connect", function(swoole_client $newCli) use($cli) { + $newCli->on("connect", function(Swoole\Client $newCli) use($cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "connected fd <$fd>, reuse!!!\n"; diff --git a/tests/include/api/swoole_http_server/http_server.php b/tests/include/api/swoole_http_server/http_server.php index b7f05c8be83..452d0797bc8 100644 --- a/tests/include/api/swoole_http_server/http_server.php +++ b/tests/include/api/swoole_http_server/http_server.php @@ -5,16 +5,16 @@ class HttpServer { /** - * @var \swoole_http_server + * @var Swoole\Http\Server */ public $httpServ; public function __construct($host = HTTP_SERVER_HOST, $port = HTTP_SERVER_PORT, $ssl = false) { if ($ssl) { - $this->httpServ = new \swoole_http_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $this->httpServ = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); } else { - $this->httpServ = new \swoole_http_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->httpServ = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); } $config = [ @@ -111,7 +111,7 @@ public function onReceive(\swoole_http_server $swooleServer, $fd, $fromId, $data $swooleServer->send($fd, RandStr::gen($recv_len, RandStr::ALL)); } - public function onRequest(\swoole_http_request $request, \swoole_http_response $response) + public function onRequest(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { $uri = $request->server["request_uri"]; if ($uri === "/favicon.ico") { @@ -220,7 +220,7 @@ public function onRequest(\swoole_http_request $request, \swoole_http_response $ } if ($uri === "/sleep") { - swoole_timer_after(1000, function() use($response) { + Swoole\Timer::after(1000, function() use($response) { $response->end(); }); return; @@ -265,11 +265,11 @@ public function onRequest(\swoole_http_request $request, \swoole_http_response $ $hexLen = dechex(strlen($str)); return $write("$hexLen\r\n$str\r\n"); }; - $timer = swoole_timer_tick(200, function() use(&$timer, $writeChunk) { + $timer = Swoole\Timer::tick(200, function() use(&$timer, $writeChunk) { static $i = 0; $str = RandStr::gen($i++ % 40 + 1, RandStr::CHINESE) . "
"; if ($writeChunk($str) === false) { - swoole_timer_clear($timer); + Swoole\Timer::clear($timer); } }); return; diff --git a/tests/include/api/swoole_http_server/http_server_without_response.php b/tests/include/api/swoole_http_server/http_server_without_response.php index 668c3c94ff5..e24150cd025 100644 --- a/tests/include/api/swoole_http_server/http_server_without_response.php +++ b/tests/include/api/swoole_http_server/http_server_without_response.php @@ -3,8 +3,8 @@ $host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST; $port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT; -$httpServer = new swoole_http_server($host, $port); +$httpServer = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS); $httpServer->on("request", function ($request, $response) { }); -$httpServer->start(); \ No newline at end of file +$httpServer->start(); diff --git a/tests/include/api/swoole_server/TestServer.php b/tests/include/api/swoole_server/TestServer.php index 1e3aae1e902..4a1f674cc0c 100644 --- a/tests/include/api/swoole_server/TestServer.php +++ b/tests/include/api/swoole_server/TestServer.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ abstract class TestServer @@ -37,7 +37,7 @@ abstract function onReceive($serv, $fd, $reactor_id, $data); function __construct(int $port, bool $base = false) { $mode = $base ? SWOOLE_BASE : SWOOLE_PROCESS; - $serv = new swoole_server('127.0.0.1', $port, $mode); + $serv = new Swoole\Server('127.0.0.1', $port, $mode); $serv->on('Connect', [$this, 'onConnect']); $serv->on('receive', [$this, '_receive']); $serv->on('workerStart', [$this, 'onWorkerStart']); diff --git a/tests/include/api/swoole_server/multi_protocol_server.php b/tests/include/api/swoole_server/multi_protocol_server.php index eb37d58379a..aab1b67a219 100644 --- a/tests/include/api/swoole_server/multi_protocol_server.php +++ b/tests/include/api/swoole_server/multi_protocol_server.php @@ -14,13 +14,13 @@ class OpcodeServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port, $port1, $port2) { - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ 'dispatch_mode' => 3, 'worker_num' => 2, @@ -29,7 +29,7 @@ public function __construct($host, $port, $port1, $port2) 'package_eof' => "\r\n", ]); - $this->swooleServer->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port) { + $this->swooleServer->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port) { Assert::same(intval($recv), $port); $r = $server->send($fd, opcode_encode("return", $port)); assert($r !== false); @@ -44,7 +44,7 @@ public function __construct($host, $port, $port1, $port2) 'package_eof' => "\r", ]); - $serv1->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port1) { + $serv1->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port1) { Assert::same(intval($recv), $port1); $r = $server->send($fd, opcode_encode("return", $port1)); assert($r !== false); @@ -60,7 +60,7 @@ public function __construct($host, $port, $port1, $port2) ]); - $serv2->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port2) { + $serv2->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port2) { Assert::same(intval($recv), $port2); $r = $server->send($fd, opcode_encode("return", $port2)); assert($r !== false); @@ -88,23 +88,23 @@ public function start($lifetime = 1000) public function onConnect() { } public function onClose() { } - public function onStart(\swoole_server $swooleServer) { } - public function onShutdown(\swoole_server $swooleServer) { } - public function onWorkerStart(\swoole_server $swooleServer, $workerId) + public function onStart(Swoole\Server $swooleServer) { } + public function onShutdown(Swoole\Server $swooleServer) { } + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { if ($workerId === 0) { - swoole_timer_after($this->lifetime, function() { + Swoole\Timer::after($this->lifetime, function() { $this->swooleServer->shutdown(); kill_self_and_descendant(getmypid()); /* - \swoole_process::signal(SIGTERM, swoole_function() { + \Swoole\Process::signal(SIGTERM, swoole_function() { $this->swooleServer->shutdown(); }); - \swoole_process::kill(0, SIGTERM); + \Swoole\Process::kill(0, SIGTERM); */ }); } } - public function onWorkerStop(\swoole_server $swooleServer, $workerId) { } - public function onWorkerError(\swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { } + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } } diff --git a/tests/include/api/swoole_server/opcode_server.php b/tests/include/api/swoole_server/opcode_server.php index 533658b0fea..19513d4eba0 100644 --- a/tests/include/api/swoole_server/opcode_server.php +++ b/tests/include/api/swoole_server/opcode_server.php @@ -14,13 +14,13 @@ class OpcodeServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port, $port1 = null, $port2 = null) { - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ 'dispatch_mode' => 3, 'worker_num' => 2, @@ -63,59 +63,31 @@ public function start($lifetime = 1000) $this->swooleServer->on('pipeMessage', [$this, 'onPipeMessage']); $this->swooleServer->on('packet', [$this, 'onPacket']); - /* - $proc = new \swoole_process(swoole_function(\swoole_process $proc) use($i) { - var_dump($this->swooleServer->id); - sleep(10000); - $r = $this->swooleServer->addProcess($proc); - var_dump($r); - $proc->freeQueue(); - }); - $proc->useQueue(); - // $proc->start(); - - $proc1 = new \swoole_process(swoole_function(\swoole_process $proc) use($i) { - var_dump($this->swooleServer->id); - sleep(1000); - }); - - $proc2 = new \swoole_process(swoole_function(\swoole_process $proc) { - var_dump($this->swooleServer->id); - sleep(1000); - }); - - - $r = $this->swooleServer->addProcess($proc); - $r = $this->swooleServer->addProcess($proc1); - $r = $this->swooleServer->addProcess($proc2); - var_dump($this->swooleServer->id); - */ - $this->swooleServer->start(); } public function onConnect() { } public function onClose() { } - public function onStart(\swoole_server $swooleServer) { } - public function onShutdown(\swoole_server $swooleServer) { } - public function onWorkerStart(\swoole_server $swooleServer, $workerId) + public function onStart(Swoole\Server $swooleServer) { } + public function onShutdown(Swoole\Server $swooleServer) { } + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { if ($workerId === 0) { - swoole_timer_after($this->lifetime, function() { + Swoole\Timer::after($this->lifetime, function() { $this->swooleServer->shutdown(); kill_self_and_descendant(getmypid()); /* - \swoole_process::signal(SIGTERM, swoole_function() { + \Swoole\Process::signal(SIGTERM, swoole_function() { $this->swooleServer->shutdown(); }); - \swoole_process::kill(0, SIGTERM); + \Swoole\Process::kill(0, SIGTERM); */ }); } } - public function onWorkerStop(\swoole_server $swooleServer, $workerId) { } - public function onWorkerError(\swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } - public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $recv) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { } + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } + public function onReceive(Swoole\Server $swooleServer, $fd, $fromReactorId, $recv) { list($op, $args) = opcode_decode($recv); @@ -138,6 +110,22 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re Assert::true($r); return; + case "shutdown": + $r = $swooleServer->shutdown(); + assert($r !== false); + Assert::true($r); + $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); + return; + + case "stop": + $r = $swooleServer->stop(); + assert($r !== false); + Assert::true($r); + $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); + return; + default: if (method_exists($swooleServer, $op)) { $r = call_user_func_array([$swooleServer, $op], $args); @@ -145,6 +133,7 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re $r = true; } $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); return; } else { @@ -152,14 +141,14 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re } } - public function onTask(\swoole_server $swooleServer, $taskId, $fromWorkerId, $recv) + public function onTask(Swoole\Server $swooleServer, $taskId, $fromWorkerId, $recv) { $recv = json_decode($recv); Assert::same(json_last_error(), JSON_ERROR_NONE); return json_encode($recv); } - public function onFinish(\swoole_server $swooleServer, $taskId, $recv) + public function onFinish(Swoole\Server $swooleServer, $taskId, $recv) { $recv = json_decode($recv); Assert::same(json_last_error(), JSON_ERROR_NONE); @@ -167,7 +156,7 @@ public function onFinish(\swoole_server $swooleServer, $taskId, $recv) $this->swooleServer->send($recv["fd"], opcode_encode("return", $recv["data"])); } - public function onPipeMessage(\swoole_server $swooleServer, $fromWorkerId, $recv) + public function onPipeMessage(Swoole\Server $swooleServer, $fromWorkerId, $recv) { $recv = json_decode($recv, true); Assert::same(json_last_error(), JSON_ERROR_NONE); @@ -175,7 +164,7 @@ public function onPipeMessage(\swoole_server $swooleServer, $fromWorkerId, $recv $this->swooleServer->send($recv["fd"], opcode_encode("return", $recv["msg"])); } - public function onPacket(\swoole_server $swooleServer, $data, array $clientInfo) + public function onPacket(Swoole\Server $swooleServer, $data, array $clientInfo) { } diff --git a/tests/include/api/swoole_server/reconnect_fail/tcp_client.php b/tests/include/api/swoole_server/reconnect_fail/tcp_client.php index f9c478124a2..edad1ee4e3d 100644 --- a/tests/include/api/swoole_server/reconnect_fail/tcp_client.php +++ b/tests/include/api/swoole_server/reconnect_fail/tcp_client.php @@ -3,17 +3,17 @@ function reconn() { echo "Reconnect\n"; - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $cli->on("connect", function(swoole_client $cli) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli->on("connect", function(Swoole\Client $cli) { // client 发送 大包数据 $cli->send(str_repeat("\0", 1024 * 1024 * 1.9)); }); - $cli->on("receive", function(swoole_client $cli, $data) { + $cli->on("receive", function(Swoole\Client $cli, $data) { $cli->send($data); }); - $cli->on("error", function(swoole_client $cli) { echo "error\n"; }); - $cli->on("close", function(swoole_client $cli) { echo "close\n"; reconn(); }); + $cli->on("error", function(Swoole\Client $cli) { echo "error\n"; }); + $cli->on("close", function(Swoole\Client $cli) { echo "close\n"; reconn(); }); $cli->connect('127.0.0.1', 9001); } -reconn(); \ No newline at end of file +reconn(); diff --git a/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php b/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php index 178369a0df4..a7a270ce09c 100644 --- a/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php +++ b/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php @@ -19,13 +19,13 @@ function debug_log($str, $handle = STDERR) class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 @@ -70,32 +70,32 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { echo "close $fd\n"; // var_dump($swooleServer->shutdown()); @@ -103,4 +103,4 @@ public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) var_dump($swooleServer->close($fd)); // $swooleServer->send($fd, $data); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/server_manager_process_exit.php b/tests/include/api/swoole_server/server_manager_process_exit.php index 5b4a5b49aec..3a0f31bd8f3 100644 --- a/tests/include/api/swoole_server/server_manager_process_exit.php +++ b/tests/include/api/swoole_server/server_manager_process_exit.php @@ -19,37 +19,37 @@ suicide(1000); - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ "socket_buffer_size" => 1024, ])); - $cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); Assert::true($cli->isConnected()); $cli->send(str_repeat("\0", 1024)); }); - $cli->on("receive", function(swoole_client $cli, $data){ + $cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(str_repeat("\0", $recv_len)); }); - $cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); - $cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); - $cli->timeo_id = swoole_timer_after(1000, function() use($cli) { + $cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); @@ -62,13 +62,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -108,35 +108,35 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { $recv_len = strlen($data); debug_log("receive: len $recv_len"); $swooleServer->send($fd, str_repeat("\0", $recv_len)); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/server_send_fast_recv_slow.php b/tests/include/api/swoole_server/server_send_fast_recv_slow.php index 0405d35b136..a65df943cbf 100644 --- a/tests/include/api/swoole_server/server_send_fast_recv_slow.php +++ b/tests/include/api/swoole_server/server_send_fast_recv_slow.php @@ -7,15 +7,15 @@ suicide(3000); - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ "socket_buffer_size" => 1, ])); - $cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); // TODO getSocket BUG // assert(is_resource($cli->getSocket())); @@ -31,24 +31,24 @@ // $cli->sendfile(__DIR__.'/test.txt'); }); - $cli->on("receive", function(swoole_client $cli, $data){ + $cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(str_repeat("\0", 1024)); }); - $cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); - $cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); - $cli->timeo_id = swoole_timer_after(1000, function() use($cli) { + $cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); @@ -63,13 +63,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -111,35 +111,35 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { $recv_len = strlen($data); debug_log("receive: len $recv_len"); $swooleServer->send($fd, str_repeat("\0", $recv_len)); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/simple_server.php b/tests/include/api/swoole_server/simple_server.php index 0795b6ede8a..25d2623ec63 100644 --- a/tests/include/api/swoole_server/simple_server.php +++ b/tests/include/api/swoole_server/simple_server.php @@ -12,13 +12,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -74,32 +74,32 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { if (trim($data) == 'shutdown') { diff --git a/tests/include/api/swoole_server/simple_tcp_server.php b/tests/include/api/swoole_server/simple_tcp_server.php index 351bec3f69b..44ec23627a9 100644 --- a/tests/include/api/swoole_server/simple_tcp_server.php +++ b/tests/include/api/swoole_server/simple_tcp_server.php @@ -12,14 +12,14 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port) { echo "swoole_server host:$host, port:$port\n"; - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "pipe_buffer_size" => 1024 * 1024 * 1024, 'dispatch_mode' => 3, @@ -59,37 +59,37 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); if ($workerId == 0) { - swoole_timer_after(5000, function () { + Swoole\Timer::after(5000, function () { $this->swooleServer->shutdown(); }); } } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { //echo "swoole_server receive data: $data\n"; $recv_len = strlen($data); diff --git a/tests/include/api/swoole_server/simple_udp_server.php b/tests/include/api/swoole_server/simple_udp_server.php index 89db793cc0b..cabfd9b4ac9 100644 --- a/tests/include/api/swoole_server/simple_udp_server.php +++ b/tests/include/api/swoole_server/simple_udp_server.php @@ -10,7 +10,7 @@ class UdpServer public function __construct() { - $this->swooleServer = new \swoole_server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + $this->swooleServer = new Swoole\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); $this->swooleServer->set([ "max_connection" => 1000, 'dispatch_mode' => 3, @@ -47,36 +47,36 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); - swoole_timer_after(3000, function() { + Swoole\Timer::after(3000, function() { $this->swooleServer->shutdown(); }); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } //UDP: 收到数据帧事件 - public function onPacket(swoole_server $swooleServer, $data, $clientInfo) + public function onPacket(Swoole\Server $swooleServer, $data, $clientInfo) { if (trim($data) == 'shutdown') { diff --git a/tests/include/api/swoole_server/tcp_serv.php b/tests/include/api/swoole_server/tcp_serv.php index 5ac57312635..e7156e6abf3 100644 --- a/tests/include/api/swoole_server/tcp_serv.php +++ b/tests/include/api/swoole_server/tcp_serv.php @@ -4,13 +4,15 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + global $argv; + + $this->swooleServer = new Swoole\Server($argv[1], $argv[2], SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 @@ -55,32 +57,32 @@ public function onClose() // print("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { // print("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { // print("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { // print("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { // print("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { // print("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { static $i; if ($i > USE_VALGRIND ? 200 : 20000) diff --git a/tests/include/api/swoole_server/tcp_task_server.php b/tests/include/api/swoole_server/tcp_task_server.php index 4b38d69826e..abfc9f4bcf9 100644 --- a/tests/include/api/swoole_server/tcp_task_server.php +++ b/tests/include/api/swoole_server/tcp_task_server.php @@ -13,7 +13,7 @@ class TcpServer public function __construct($host, $port) { echo "swoole_server host:$host, port:$port\n"; - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "pipe_buffer_size" => 1024 * 1024 * 1024, 'dispatch_mode' => 3, @@ -57,37 +57,37 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); if ($workerId == 0) { - //swoole_timer_after(5000, function () { + //Swoole\Timer::after(5000, function () { // $this->swooleServer->shutdown(); //}); } } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { //echo "swoole_server receive data: $data\n"; $param = array( @@ -98,13 +98,13 @@ public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) //echo "send data to task worker.\n"; } - public function onTask(swoole_server $swooleServer, $task_id, $fromId, $data) + public function onTask(Swoole\Server $swooleServer, $task_id, $fromId, $data) { $task_data = json_decode($data, true); $swooleServer->finish($task_data); } - public function onFinish(swoole_server $swooleServer, $worker_task_id, $task_data) + public function onFinish(Swoole\Server $swooleServer, $worker_task_id, $task_data) { $swooleServer->send($task_data['fd'], "OK"); } diff --git a/tests/include/api/swoole_thread/putenv.php b/tests/include/api/swoole_thread/putenv.php new file mode 100644 index 00000000000..b82fee992b6 --- /dev/null +++ b/tests/include/api/swoole_thread/putenv.php @@ -0,0 +1,6 @@ +webSocketServ = new \swoole_websocket_server($host, $port); + $this->webSocketServ = new Swoole\WebSocket\Server($host, $port, SWOOLE_PROCESS); $this->webSocketServ->set([ // 输出限制 @@ -65,42 +65,42 @@ public function onClose() debug_log("closing ....."); } - public function onStart(\swoole_websocket_server $swooleServer) + public function onStart(Swoole\WebSocket\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(\swoole_websocket_server $swooleServer) + public function onShutdown(Swoole\WebSocket\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(\swoole_websocket_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\WebSocket\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(\swoole_websocket_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\WebSocket\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(\swoole_websocket_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\WebSocket\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onRequest(\swoole_http_request $request, \swoole_http_response $response) + public function onRequest(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { $response->end("Hello World!"); } - public function onOpen(swoole_websocket_server $server, $request) + public function onOpen(Swoole\WebSocket\Server $server, $request) { debug_log("{$request->fd} opened"); } - public function onMessage(swoole_websocket_server $server, $frame) + public function onMessage(Swoole\WebSocket\Server $server, $frame) { $server->push($frame->fd, "SUCCESS"); } @@ -110,4 +110,4 @@ public function onMessage(swoole_websocket_server $server, $frame) $port = isset($argv[2]) ? $argv[2] : WEBSOCKET_SERVER_PORT; $wsServer = new WebSocketServer($host, $port); -$wsServer->start(); \ No newline at end of file +$wsServer->start(); diff --git a/tests/include/api/swoole_websocket_server/websocket_client.php b/tests/include/api/swoole_websocket_server/websocket_client.php index 5af8cb9aa7d..faf8fd7ac9c 100644 --- a/tests/include/api/swoole_websocket_server/websocket_client.php +++ b/tests/include/api/swoole_websocket_server/websocket_client.php @@ -57,7 +57,7 @@ function __destruct() */ public function connect() { - $this->socket = new \swoole_client(SWOOLE_SOCK_TCP); + $this->socket = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$this->socket->connect($this->host, $this->port)) { return false; diff --git a/tests/include/api/syntax_error.txt b/tests/include/api/syntax_error.txt new file mode 100644 index 00000000000..b06ab515f5a --- /dev/null +++ b/tests/include/api/syntax_error.txt @@ -0,0 +1,2 @@ +getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); +$serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ // 'log_file' => __DIR__ . '/simple_server.log', 'dispatch_mode' => 2, @@ -7,7 +7,7 @@ 'worker_num' => 1, ]); -$serv->on('workerStart', function (\swoole_server $serv) +$serv->on('workerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -16,7 +16,7 @@ $pm->wakeup(); }); -$serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { if (trim($data) == 'shutdown') { diff --git a/tests/include/api/test_classes/A2.php b/tests/include/api/test_classes/A2.php new file mode 100644 index 00000000000..6835453d5d5 --- /dev/null +++ b/tests/include/api/test_classes/A2.php @@ -0,0 +1,2 @@ + 5 ]); + if (empty(getenv('SWOOLE_DEBUG'))) { Co::set([ 'log_level' => SWOOLE_LOG_INFO, @@ -34,6 +35,20 @@ ]); } +$traceFlags = getenv('SWOOLE_TRACE_FLAGS'); +if ($traceFlags) { + $_traceFlags = 0; + if (is_numeric($traceFlags)) { + $_traceFlags = intval($traceFlags); + } else { + eval('$_traceFlags = ' . $traceFlags . ';'); + } + Co::set([ + 'log_level' => 0, + 'trace_flags' => $_traceFlags + ]); +} + // Components require __DIR__ . '/lib/vendor/autoload.php'; diff --git a/tests/include/config.php b/tests/include/config.php index 6e7058ee829..ffd8dd685d5 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -11,23 +11,22 @@ /** ============== Env =============== */ define('IS_MAC_OS', stripos(PHP_OS, 'Darwin') !== false); -define('IS_IN_TRAVIS', !!getenv('TRAVIS') || file_exists('/.travisenv')); +define('IS_IN_CI', file_exists('/.cienv')); define('IS_PHPTESTSING', !!getenv('PHPT')); define('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0'); define('HAS_SSL', defined("SWOOLE_SSL")); -define('HAS_ASYNC_REDIS', class_exists("swoole_redis", false)); -define('HAS_HTTP2', class_exists("swoole_http2_request", false)); +define('HAS_HTTP2', class_exists("Swoole\\Http2\\Request", false)); define('DEV_NULL', '/dev/null'); /** ============== Files ============== */ -define('SOURCE_ROOT_PATH', __DIR__ . '/../../'); -define('TESTS_ROOT_PATH', __DIR__ . '/../'); +define('SOURCE_ROOT_PATH', realpath(__DIR__ . '/../../')); +define('TESTS_ROOT_PATH', realpath(__DIR__ . '/../')); define('TESTS_API_PATH', TESTS_ROOT_PATH.'/include/api'); define('TESTS_LIB_PATH', TESTS_ROOT_PATH.'/include/lib'); define('TRAVIS_DIR_PATH', __DIR__ . '/../../travis/'); define('TEST_IMAGE', __DIR__ . '/../../examples/test.jpg'); define('TEST_LINK_IMAGE', __DIR__ . '/../../examples/test_link.jpg'); -define('TEST_IMAGE2', __DIR__ . '/../../examples/logo.svg'); +define('TEST_IMAGE2', __DIR__ . '/../../docs/swoole-logo.svg'); define('TEST_LOG_FILE', '/tmp/swoole.log'); define('TEST_PID_FILE', '/tmp/swoole.pid'); define('SSL_FILE_DIR', __DIR__ . '/ssl_certs/'); @@ -51,24 +50,55 @@ /** ============== MySQL ============== */ define('MYSQL_SERVER_PATH', getenv('MYSQL_SERVER_PATH') ?: - (IS_IN_TRAVIS ? TRAVIS_DIR_PATH . '/data/run/mysqld/mysqld.sock' : + (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/mysqld/mysqld.sock' : (IS_MAC_OS ? '/tmp/mysql.sock' : '/var/run/mysqld/mysqld.sock'))); -define('MYSQL_SERVER_HOST', getenv('MYSQL_SERVER_HOST') ?: (IS_IN_TRAVIS ? 'mysql' : '127.0.0.1')); +define('MYSQL_SERVER_HOST', getenv('MYSQL_SERVER_HOST') ?: (IS_IN_CI ? 'mysql' : '127.0.0.1')); define('MYSQL_SERVER_PORT', (int)(getenv('MYSQL_SERVER_PORT') ?: 3306)); define('MYSQL_SERVER_USER', getenv('MYSQL_SERVER_USER') ?: 'root'); define('MYSQL_SERVER_PWD', getenv('MYSQL_SERVER_PWD') ?: 'root'); define('MYSQL_SERVER_DB', getenv('MYSQL_SERVER_DB') ?: 'test'); +/** ============== PostgreSQL ============== */ +if (IS_IN_CI) { + define('PGSQL_HOST', 'pgsql'); + define('PGSQL_USER', 'root'); + define('PGSQL_PASSWORD', 'root'); + define('PGSQL_DBNAME', 'test'); +} else { + define('PGSQL_HOST', '127.0.0.1'); + define('PGSQL_USER', 'postgres'); + define('PGSQL_PASSWORD', 'postgres'); + define('PGSQL_DBNAME', 'postgres'); +} +define('PGSQL_PORT', '5432'); + +define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: + ('host=' . PGSQL_HOST . ' port=' . PGSQL_PORT . ' dbname=' . PGSQL_DBNAME . ' user=' . PGSQL_USER . ' password=' . PGSQL_PASSWORD)); + +/** ============== Oracle ============== */ +define('ORACLE_PORT', '1521'); +define('ORACLE_SERVICE_NAME', 'freepdb1'); +define('ORACLE_USER', 'system'); +define('ORACLE_PASSWORD', 'oracle'); +if (IS_IN_CI) { + define('ORACLE_TNS', 'oci:dbname=oracle:'.ORACLE_PORT.'/'.ORACLE_SERVICE_NAME.';charset=AL32UTF8'); +} else { + define('ORACLE_TNS', 'oci:dbname=127.0.0.1:'.ORACLE_PORT.'/'.ORACLE_SERVICE_NAME.';charset=AL32UTF8'); +} + +/** ============== Sqlite ============== */ +define('SQLITE_DSN', 'sqlite::memory:'); + /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: - (IS_IN_TRAVIS ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : + (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : (IS_MAC_OS ? '/tmp/redis.sock' : '/var/run/redis/redis-server.sock'))); -define('REDIS_SERVER_HOST', getenv('REDIS_SERVER_HOST') ?: (IS_IN_TRAVIS ? 'redis' : '127.0.0.1')); +define('REDIS_SERVER_HOST', getenv('REDIS_SERVER_HOST') ?: (IS_IN_CI ? 'redis' : '127.0.0.1')); define('REDIS_SERVER_PORT', (int)(getenv('REDIS_SERVER_PORT') ?: 6379)); define('REDIS_SERVER_PWD', getenv('REDIS_SERVER_PWD') ?: 'root'); define('REDIS_SERVER_DB', (int)(getenv('REDIS_SERVER_DB') ?: 0)); -if (!getenv('SWOOLE_TEST_NO_DOCKER')) { +if (getenv('SWOOLE_TEST_IN_DOCKER')) { if (!empty($info = `docker ps 2>&1 | grep httpbin 2>&1`) && preg_match('/\s+?[^:]+:(\d+)->\d+\/tcp\s+/', $info, $matches) && is_numeric($matches[1])) { @@ -76,8 +106,17 @@ } } +/** ============== ODBC ============== */ +if (IS_IN_CI) { + define('ODBC_DSN', 'odbc:mysql-test'); +} else { + define('ODBC_DSN', 'odbc:mysql-test'); +} + +define('SWOOLE_TEST_ECHO', empty(getenv('SWOOLE_TEST_NO_ECHO'))); + /** ============== HttpBin ============== */ -if (IS_IN_TRAVIS) { +if (IS_IN_CI) { define('HTTPBIN_SERVER_HOST', 'httpbin'); define('HTTPBIN_SERVER_PORT', 80); define('HTTPBIN_LOCALLY', true); @@ -94,21 +133,44 @@ define('HTTPBIN_SERVER_PORT', 80); } +if (IS_IN_CI) { + define('TEST_HTTP2_SERVERPUSH_URL', 'https://golang-h2demo:4430/serverpush'); + define('TEST_NAME_RESOLVER', [ + 'class' => Swoole\NameResolver\Redis::class, + 'server_url' => 'tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT, + ]); +} else { + define('TEST_HTTP2_SERVERPUSH_URL', 'https://127.0.0.1:4430/serverpush'); + define('TEST_NAME_RESOLVER', [ + 'class' => Swoole\NameResolver\Consul::class, + 'server_url' => 'http://127.0.0.1:8500', + ]); +} + +if (IS_IN_CI) { + define('TEST_DOMAIN_1', 'www.google.com'); + define('TEST_DOMAIN_2', 'www.yahoo.com'); +} else { + define('TEST_DOMAIN_1', 'www.baidu.com'); + define('TEST_DOMAIN_2', 'www.qq.com'); +} + /** =============== IP ================ */ define('IP_REGEX', '/^(?:[\d]{1,3}\.){3}[\d]{1,3}$/'); /** ============= Proxy ============== */ -define('HTTP_PROXY_HOST', IS_IN_TRAVIS ? 'tinyproxy' : '127.0.0.1'); -define('HTTP_PROXY_PORT', IS_IN_TRAVIS ? 8888 : (IS_MAC_OS ? 1087 : 8888)); -define('SOCKS5_PROXY_HOST', IS_IN_TRAVIS ? 'socks5' : '127.0.0.1'); +define('HTTP_PROXY_HOST', IS_IN_CI ? 'tinyproxy' : '127.0.0.1'); +define('HTTP_PROXY_PORT', IS_IN_CI ? 8888 : (IS_MAC_OS ? 1087 : 8888)); +define('SOCKS5_PROXY_HOST', IS_IN_CI ? 'socks5' : '127.0.0.1'); define('SOCKS5_PROXY_PORT', IS_MAC_OS ? 1086 : 1080); + /** ============== Pressure ============== */ define('PRESSURE_LOW', 1); define('PRESSURE_MID', 2); define('PRESSURE_NORMAL', 3); define('PRESSURE_LEVEL', - USE_VALGRIND ? (IS_IN_TRAVIS ? PRESSURE_LOW - 1 : PRESSURE_LOW) : ((IS_IN_TRAVIS || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)); + USE_VALGRIND ? (IS_IN_CI ? PRESSURE_LOW - 1 : PRESSURE_LOW) : ((IS_IN_CI || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)); /** ============== Time ============== */ define('SERVER_PREHEATING_TIME', 0.1); @@ -124,3 +186,9 @@ define('MAX_LOOPS', [12, 24, 100, 1000][PRESSURE_LEVEL] * 1000); define('MAX_PROCESS_NUM', [2, 4, 6, 8][PRESSURE_LEVEL]); define('MAX_PACKET_NUM', [1024, 2048, 4096, 10000][PRESSURE_LEVEL]); + +/** ============== FTP ============== */ +define('FTP_HOST', IS_IN_CI ? 'ftp' : '127.0.0.1'); +define('FTP_PORT', 21); +define('FTP_USER', 'admin'); +define('FTP_PASS', 'admin'); diff --git a/tests/include/functions.php b/tests/include/functions.php index a57b77d59d5..8efafaaed93 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -88,6 +88,11 @@ function get_one_free_port(): int return $port; } +function get_constant_port(string $str, int $base = 9500): int +{ + return $base + crc32($str) % 10000; +} + function get_one_free_port_ipv6(): int { $hookFlags = Swoole\Runtime::getHookFlags(); @@ -148,10 +153,18 @@ function array_random(array $array) function phpt_echo(...$args) { + if (!SWOOLE_TEST_ECHO) { + return; + } global $argv; if (substr($argv[0], -5) === '.phpt') { foreach ($args as $arg) { - echo $arg; + if (!is_string($arg)) { + var_export($arg); + echo PHP_EOL; + } else { + echo $arg; + } } } } @@ -164,6 +177,15 @@ function phpt_var_dump(...$args) } } +function phpt_show_usage() +{ + global $argv; + if (substr($argv[0], -5) === '.phpt') { + var_dump('memory:' . memory_get_usage()); + var_dump('coroutine:' . var_export(Co::stats(), 1)); + } +} + function httpPost($url, $data) { $ch = curl_init(); @@ -363,7 +385,7 @@ function get_big_random(int $length = 1024 * 1024) return str_repeat(get_safe_random(1024), $length / 1024); } -function makeCoTcpClient($host, $port, callable $onConnect = null, callable $onReceive = null) +function makeCoTcpClient($host, $port, ?callable $onConnect = null, ?callable $onReceive = null) { go(function () use ($host, $port, $onConnect, $onReceive) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); @@ -440,9 +462,9 @@ function killself_in_syncmode($lifetime = 1000, $sig = SIGKILL) * @param callable $cb * @return mixed */ -function suicide($lifetime, $sig = SIGKILL, callable $cb = null) +function suicide($lifetime, $sig = SIGKILL, ?callable $cb = null) { - return swoole_timer_after($lifetime, function () use ($lifetime, $sig, $cb) { + return Swoole\Timer::after($lifetime, function () use ($lifetime, $sig, $cb) { if ($cb) { $cb(); } @@ -497,8 +519,11 @@ function pstree() } $y = function ($pid, $path = []) use (&$y, $pinfo) { if (isset($pinfo[$pid])) { - list($ppid,) = $pinfo[$pid]; - $ppid = $ppid; + if (isset($pinfo[$pid][0])) { + list($ppid,) = $pinfo[$pid]; + } else { + $ppid = null; + } $path[] = $pid; return $y($ppid, $path); } else { @@ -615,7 +640,7 @@ function start_server($file, $host, $port, $redirect_file = "/dev/null", $ext1 = return function () use ($handle, $redirect_file) { // @unlink($redirect_file); proc_terminate($handle, SIGTERM); - swoole_event_exit(); + Swoole\Event::exit(); exit; }; } @@ -629,7 +654,7 @@ function swoole_fork_exec(callable $fn, bool $redirect_stdin_and_stdout = false, return $process::wait(); } -function fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) +function php_fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) { $pid = pcntl_fork(); if ($pid < 0) { @@ -646,6 +671,7 @@ function fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) exit; } pcntl_waitpid($pid, $status); + return ['pid' => $pid, 'status', $status]; } /** @@ -658,7 +684,7 @@ function fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) * @param array|null $env env * @return array [out, err] */ -function spawn_exec($cmd, $input = null, $tv_sec = null, $tv_usec = null, $cwd = null, array $env = null) +function spawn_exec($cmd, $input = null, $tv_sec = null, $tv_usec = null, $cwd = null, ?array $env = null) { $out = $err = null; $winOpt = ['suppress_errors' => true, 'binary_pipes' => true]; @@ -804,3 +830,45 @@ function assert_server_stats($stats) { Assert::keyExists($stats, 'connection_num'); Assert::keyExists($stats, 'request_count'); } + +function assert_upload_file($file, $tmp_name, $name, $type, $size, $error = 0) +{ + Assert::notEmpty($file); + Assert::eq($file['tmp_name'], $tmp_name); + Assert::eq($file['name'], $name); + Assert::eq($file['type'], $type); + Assert::eq($file['size'], $size); + Assert::eq($file['error'], $error); +} + +function swoole_loop_n($n, $fn) +{ + for ($i = 0; $i < $n; $i++) { + $fn($i); + } +} + +function swoole_loop($fn) +{ + $i = 0; + while (true) { + $fn($i++); + } +} + +function build_ftp_url(string $path = ''): string +{ + return 'ftp://' . FTP_USER . ':' . FTP_PASS . '@' . FTP_HOST . ':' . FTP_PORT . '/' . $path; +} + +function get_thread_name(): string +{ + return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . \Swoole\Thread::getNativeId() . '/comm')); +} + +function mkdir_if_not_exists(string $string): void +{ + if (!is_dir($string)) { + mkdir($string, 0777, true); + } +} diff --git a/tests/include/lib/composer.json b/tests/include/lib/composer.json index 4318fbb6d24..634505deba8 100644 --- a/tests/include/lib/composer.json +++ b/tests/include/lib/composer.json @@ -22,7 +22,15 @@ "php-http/httplug": "^1.1|^2.0", "php-http/message": "^1.5", "psr/http-factory": "^1.0", - "symfony/http-client": "*", - "nyholm/psr7": "^1.4" + "symfony/http-client": "^5.3", + "nyholm/psr7": "^1.4", + "friendsofphp/php-cs-fixer": "^3.3", + "php-http/message-factory": "^1.1", + "predis/predis": "^2.2" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } } } diff --git a/tests/include/lib/composer.lock b/tests/include/lib/composer.lock deleted file mode 100644 index 83184e298e2..00000000000 --- a/tests/include/lib/composer.lock +++ /dev/null @@ -1,1773 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "fd2d63ec611fa1880d98b39fab449415", - "packages": [ - { - "name": "clue/stream-filter", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/clue/stream-filter.git", - "reference": "aeb7d8ea49c7963d3b581378955dbf5bc49aa320" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/stream-filter/zipball/aeb7d8ea49c7963d3b581378955dbf5bc49aa320", - "reference": "aeb7d8ea49c7963d3b581378955dbf5bc49aa320", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\StreamFilter\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "A simple and modern approach to stream filtering in PHP", - "homepage": "https://github.com/clue/php-stream-filter", - "keywords": [ - "bucket brigade", - "callback", - "filter", - "php_user_filter", - "stream", - "stream_filter_append", - "stream_filter_register" - ], - "support": { - "issues": "https://github.com/clue/stream-filter/issues", - "source": "https://github.com/clue/stream-filter/tree/v1.5.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-10-02T12:38:20+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.3.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "7008573787b430c1c1f650e3722d9bba59967628" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", - "reference": "7008573787b430c1c1f650e3722d9bba59967628", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7 || ^2.0", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.3-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.3.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://github.com/alexeyshockov", - "type": "github" - }, - { - "url": "https://github.com/gmponos", - "type": "github" - } - ], - "time": "2021-03-23T11:33:13+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" - }, - "time": "2021-03-07T09:25:29+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.8.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" - }, - "time": "2021-04-26T09:17:50+00:00" - }, - { - "name": "nyholm/psr7", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/Nyholm/psr7.git", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1", - "php-http/message-factory": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "http-interop/http-factory-tests": "^0.9", - "php-http/psr7-integration-tests": "^1.0", - "phpunit/phpunit": "^7.5 || 8.5 || 9.4", - "symfony/error-handler": "^4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "Nyholm\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - }, - { - "name": "Martijn van der Ven", - "email": "martijn@vanderven.se" - } - ], - "description": "A fast PHP7 implementation of PSR-7", - "homepage": "https://tnyholm.se", - "keywords": [ - "psr-17", - "psr-7" - ], - "support": { - "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.4.1" - }, - "funding": [ - { - "url": "https://github.com/Zegnat", - "type": "github" - }, - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], - "time": "2021-07-02T08:32:20+00:00" - }, - { - "name": "php-http/client-common", - "version": "2.4.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/client-common.git", - "reference": "29e0c60d982f04017069483e832b92074d0a90b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/client-common/zipball/29e0c60d982f04017069483e832b92074d0a90b2", - "reference": "29e0c60d982f04017069483e832b92074d0a90b2", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0", - "php-http/httplug": "^2.0", - "php-http/message": "^1.6", - "php-http/message-factory": "^1.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "symfony/options-resolver": "^2.6 || ^3.4.20 || ~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0", - "symfony/polyfill-php80": "^1.17" - }, - "require-dev": { - "doctrine/instantiator": "^1.1", - "guzzlehttp/psr7": "^1.4", - "nyholm/psr7": "^1.2", - "phpspec/phpspec": "^5.1 || ^6.0", - "phpspec/prophecy": "^1.10.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" - }, - "suggest": { - "ext-json": "To detect JSON responses with the ContentTypePlugin", - "ext-libxml": "To detect XML responses with the ContentTypePlugin", - "php-http/cache-plugin": "PSR-6 Cache plugin", - "php-http/logger-plugin": "PSR-3 Logger plugin", - "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\Common\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Common HTTP Client implementations and tools for HTTPlug", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "common", - "http", - "httplug" - ], - "support": { - "issues": "https://github.com/php-http/client-common/issues", - "source": "https://github.com/php-http/client-common/tree/2.4.0" - }, - "time": "2021-07-05T08:19:25+00:00" - }, - { - "name": "php-http/discovery", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/778f722e29250c1fac0bbdef2c122fa5d038c9eb", - "reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "nyholm/psr7": "<1.0" - }, - "require-dev": { - "graham-campbell/phpspec-skip-example-extension": "^5.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^5.1 || ^6.1", - "puli/composer-plugin": "1.0.0-beta10" - }, - "suggest": { - "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Discovery\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Finds installed HTTPlug implementations and PSR-7 message factories", - "homepage": "http://php-http.org", - "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr7" - ], - "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.14.0" - }, - "time": "2021-06-01T14:30:21+00:00" - }, - { - "name": "php-http/httplug", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/httplug.git", - "reference": "191a0a1b41ed026b717421931f8d3bd2514ffbf9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/191a0a1b41ed026b717421931f8d3bd2514ffbf9", - "reference": "191a0a1b41ed026b717421931f8d3bd2514ffbf9", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0", - "php-http/promise": "^1.1", - "psr/http-client": "^1.0", - "psr/http-message": "^1.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1", - "phpspec/phpspec": "^5.1 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "HTTPlug, the HTTP client abstraction for PHP", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "http" - ], - "support": { - "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/master" - }, - "time": "2020-07-13T15:43:23+00:00" - }, - { - "name": "php-http/message", - "version": "1.11.2", - "source": { - "type": "git", - "url": "https://github.com/php-http/message.git", - "reference": "295c82867d07261f2fa4b3a26677519fc6f7f5f6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/295c82867d07261f2fa4b3a26677519fc6f7f5f6", - "reference": "295c82867d07261f2fa4b3a26677519fc6f7f5f6", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "clue/stream-filter": "^1.5", - "php": "^7.1 || ^8.0", - "php-http/message-factory": "^1.0.2", - "psr/http-message": "^1.0" - }, - "provide": { - "php-http/message-factory-implementation": "1.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.6", - "ext-zlib": "*", - "guzzlehttp/psr7": "^1.0", - "laminas/laminas-diactoros": "^2.0", - "phpspec/phpspec": "^5.1 || ^6.3", - "slim/slim": "^3.0" - }, - "suggest": { - "ext-zlib": "Used with compressor/decompressor streams", - "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", - "laminas/laminas-diactoros": "Used with Diactoros Factories", - "slim/slim": "Used with Slim Framework PSR-7 implementation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - }, - "files": [ - "src/filters.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "HTTP Message related tools", - "homepage": "http://php-http.org", - "keywords": [ - "http", - "message", - "psr-7" - ], - "support": { - "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.11.2" - }, - "time": "2021-08-03T11:52:11+00:00" - }, - { - "name": "php-http/message-factory", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-http/message-factory.git", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.4", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Factory interfaces for PSR-7 HTTP Message", - "homepage": "http://php-http.org", - "keywords": [ - "factory", - "http", - "message", - "stream", - "uri" - ], - "support": { - "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/master" - }, - "time": "2015-12-19T14:08:53+00:00" - }, - { - "name": "php-http/promise", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/promise.git", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", - "phpspec/phpspec": "^5.1.2 || ^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Joel Wurtz", - "email": "joel.wurtz@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Promise used for asynchronous HTTP requests", - "homepage": "http://httplug.io", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.1.0" - }, - "time": "2020-07-07T09:29:14+00:00" - }, - { - "name": "psr/container", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" - }, - "time": "2021-03-05T17:36:06+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/master" - }, - "time": "2020-06-29T06:28:15+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" - }, - "time": "2019-04-30T12:38:16+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-03-23T23:28:01+00:00" - }, - { - "name": "symfony/http-client", - "version": "v5.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/67c177d4df8601d9a71f9d615c52171c98d22d74", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/http-kernel": "^4.4.13|^5.1.5", - "symfony/process": "^4.4|^5.0", - "symfony/stopwatch": "^4.4|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-client/tree/v5.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-23T15:55:36+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-04-11T23:07:08+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v5.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a603e5701bd6e305cfc777a8b50bf081ef73105e", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-23T15:55:36+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-28T13:41:28+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-04-01T10:43:52+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.0.0" -} diff --git a/tests/include/lib/src/ChildProcess.php b/tests/include/lib/src/ChildProcess.php new file mode 100644 index 00000000000..dd4905f3b13 --- /dev/null +++ b/tests/include/lib/src/ChildProcess.php @@ -0,0 +1,33 @@ +process = new Process(function (Process $worker) use ($script) { + $worker->exec('/bin/sh', ['-c', $script]); + }, true, SOCK_STREAM, false); + $this->process->start(); + } + + public function read() + { + return $this->process->read(); + } + + public function write(string $data): void + { + $this->process->write($data); + } + + static function exec(string $script): ChildProcess + { + return new self($script); + } +} diff --git a/tests/include/lib/src/CurlManager.php b/tests/include/lib/src/CurlManager.php index 9b593554363..acf69a63b9e 100644 --- a/tests/include/lib/src/CurlManager.php +++ b/tests/include/lib/src/CurlManager.php @@ -67,4 +67,4 @@ function run(callable $fn, $createCliServer = true) Process::wait(); } } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/DbWrapper.php b/tests/include/lib/src/DbWrapper.php index 8309875b937..ad6921292e0 100644 --- a/tests/include/lib/src/DbWrapper.php +++ b/tests/include/lib/src/DbWrapper.php @@ -15,8 +15,14 @@ class DbWrapper public function connect($config) { - $mysql = new MySQL([]); - $res = $mysql->connect($config); + $mysql = new \mysqli(); + $res = $mysql->connect( + $config['host'], + $config['user'], + $config['password'], + $config['database'], + $config['port'], + ); if (false === $res) { throw new RuntimeException($mysql->connect_error, $mysql->errno); @@ -31,20 +37,18 @@ public function connect($config) public function __call($name, $arguments) { // $result = $this->mysql->{$name}(...$arguments); - $result = call_user_func_array([$this->mysql, $name], $arguments); + // $result = call_user_func_array([$this->mysql, $name], $arguments); $result = $this->mysql->query($arguments[0]); if (false === $result) { if (!$this->mysql->connected) { - $this->mysql->connect($this->config); - + $this->connect($this->config); return call_user_func_array([$this->mysql, $name], $arguments); } - - if (!empty($this->mysql->errno)) { //有错误码,则抛出弃常 + if (!empty($this->mysql->errno)) { throw new RuntimeException($this->mysql->error, $this->mysql->errno); } } - return $result; + return $result->fetch_all(); } } diff --git a/tests/include/lib/src/LengthServer.php b/tests/include/lib/src/LengthServer.php index 0f0cc1ebe35..7f03f49bee1 100644 --- a/tests/include/lib/src/LengthServer.php +++ b/tests/include/lib/src/LengthServer.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/tests/include/lib/src/MysqlPool.php b/tests/include/lib/src/MysqlPool.php index 00f2484901c..153094157f3 100644 --- a/tests/include/lib/src/MysqlPool.php +++ b/tests/include/lib/src/MysqlPool.php @@ -53,7 +53,7 @@ public function put($mySQL) public function get() { /** - * @var \Swoole\Coroutine\Mysql $mysql + * @var mysqli */ $mysql = $this->pool->pop($this->config['pool_get_timeout']); if ($mysql === false) { diff --git a/tests/include/lib/src/ProcessManager.php b/tests/include/lib/src/ProcessManager.php index cf301f201d9..3c190cbe266 100644 --- a/tests/include/lib/src/ProcessManager.php +++ b/tests/include/lib/src/ProcessManager.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -49,7 +49,7 @@ class ProcessManager public $useConstantPorts = false; protected $childPid; - protected $childStatus = 255; + protected $childExitStatus = 255; protected $expectExitSignal = [0, SIGTERM]; protected $parentFirst = false; protected $killed = false; @@ -251,14 +251,19 @@ public function kill(bool $force = false) } } - public function initFreePorts(int $num = 1) + /** + * @param int $num + * @param int $increment Only used for constant port number, must be a constant + * @return void + */ + public function initFreePorts(int $num = 1, int $increment = 0): void { for ($i = $num; $i--;) { - $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts)) : get_one_free_port(); + $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts) + $increment) : get_one_free_port(); } } - public function initFreeIPv6Ports(int $num = 1) + public function initFreeIPv6Ports(int $num = 1): void { for ($i = $num; $i--;) { $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts)) : get_one_free_port_ipv6(); @@ -309,7 +314,7 @@ public function run($redirectStdout = false) $this->runParentFunc($this->childPid = $this->childProcess->pid); Event::wait(); $waitInfo = Process::wait(true); - $this->childStatus = $waitInfo['code']; + $this->childExitStatus = $waitInfo['code']; if (!in_array($waitInfo['signal'], $this->expectExitSignal)) { throw new RuntimeException("Unexpected exit code {$waitInfo['signal']}"); } @@ -337,11 +342,15 @@ public function expectExitCode($code = 0) if (!is_array($code)) { $code = [$code]; } - if (!in_array($this->childStatus, $code)) { - throw new RuntimeException("Unexpected exit code {$this->childStatus}"); + if (!in_array($this->childExitStatus, $code)) { + throw new RuntimeException("Unexpected exit code {$this->childExitStatus}"); } } + function getChildExitStatus() { + return $this->childExitStatus; + } + public function setExpectExitSignal($signal = 0) { if (!is_array($signal)) { @@ -349,4 +358,19 @@ public function setExpectExitSignal($signal = 0) } $this->expectExitSignal = $signal; } + + static function exec(callable $fn) + { + $pm = new static(); + $pm->setWaitTimeout(0); + $pm->parentFunc = function () { + }; + $pm->childFunc = function () use ($pm, $fn) { + $fn($pm); + }; + $pm->childFirst(); + $pm->run(true); + + return $pm; + } } diff --git a/tests/include/lib/src/RandStr.php b/tests/include/lib/src/RandStr.php index e5ee561944c..0a4e810662e 100644 --- a/tests/include/lib/src/RandStr.php +++ b/tests/include/lib/src/RandStr.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -98,4 +98,4 @@ private static function getChars($mask) static::$strCache[$mask] = $str; return $str; } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/Redis/SQLPool.php b/tests/include/lib/src/Redis/SQLPool.php index de4de6924e8..ea98d2a8bf5 100644 --- a/tests/include/lib/src/Redis/SQLPool.php +++ b/tests/include/lib/src/Redis/SQLPool.php @@ -2,21 +2,22 @@ namespace SwooleTest\Redis; -Class SQLPool +class SQLPool { + /** + * @var self + */ private static $instance; - public static function init() - { - self::$instance = new self; - } - /** * @param string $name * @return \SplQueue */ public static function i(string $name): \SplQueue { + if (!self::$instance) { + self::$instance = new self; + } return self::$instance->$name ?? (self::$instance->$name = new \SplQueue); } diff --git a/tests/include/lib/src/Samtleben/WebsocketClient.php b/tests/include/lib/src/Samtleben/WebsocketClient.php index ad6beb703ab..d8d0e3c5220 100644 --- a/tests/include/lib/src/Samtleben/WebsocketClient.php +++ b/tests/include/lib/src/Samtleben/WebsocketClient.php @@ -63,39 +63,38 @@ public function recvData() { } public function connect($host, $port, $path, $origin = false) - { - $this->_host = $host; - $this->_port = $port; - $this->_path = $path; - $this->_origin = $origin; - - $key = base64_encode($this->_generateRandomString(16, false, true)); - $header = "GET " . $path . " HTTP/1.1\r\n"; - $header.= "Host: ".$host.":".$port."\r\n"; - $header.= "Upgrade: websocket\r\n"; - $header.= "Connection: Upgrade\r\n"; - $header.= "Sec-WebSocket-Key: " . $key . "\r\n"; - if($origin !== false) - { - $header.= "Sec-WebSocket-Origin: " . $origin . "\r\n"; - } - $header.= "Sec-WebSocket-Version: 13\r\n"; - - $this->_Socket = new \Swoole\Client(SWOOLE_TCP, SWOOLE_SOCK_SYNC); - $this->_Socket->connect($host, $port, 1); - $this->_Socket->send($header."\r\n"); - $response = $this->_Socket->recv(1500); - - preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches); - - if ($matches) { - $keyAccept = trim($matches[1]); - $expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); - $this->_connected = ($keyAccept === $expectedResonse) ? true : false; - } - - return $this->_connected; - } + { + $this->_host = $host; + $this->_port = $port; + $this->_path = $path; + $this->_origin = $origin; + + $key = base64_encode($this->_generateRandomString(16, false, true)); + $header = "GET " . $path . " HTTP/1.1\r\n"; + $header .= "Host: " . $host . ":" . $port . "\r\n"; + $header .= "Upgrade: websocket\r\n"; + $header .= "Connection: Upgrade\r\n"; + $header .= "Sec-WebSocket-Key: " . $key . "\r\n"; + if ($origin !== false) { + $header .= "Sec-WebSocket-Origin: " . $origin . "\r\n"; + } + $header .= "Sec-WebSocket-Version: 13\r\n"; + + $this->_Socket = new \Swoole\Client(SWOOLE_TCP, SWOOLE_SOCK_SYNC); + $this->_Socket->connect($host, $port, 1); + $this->_Socket->send($header . "\r\n"); + $response = $this->_Socket->recv(1500); + + preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches); + + if ($matches) { + $keyAccept = trim($matches[1]); + $expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); + $this->_connected = ($keyAccept === $expectedResonse) ? true : false; + } + + return $this->_connected; + } public function checkConnection() { diff --git a/tests/include/lib/src/ServerManager.php b/tests/include/lib/src/ServerManager.php index 0c710697f84..f6bed10430b 100644 --- a/tests/include/lib/src/ServerManager.php +++ b/tests/include/lib/src/ServerManager.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -50,4 +50,4 @@ function run($debug = false) { return start_server($this->file, $this->host, $this->port, "/dev/null", null, null, $debug); } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/TcpStat.php b/tests/include/lib/src/TcpStat.php index 5028117c573..d485fe06c16 100644 --- a/tests/include/lib/src/TcpStat.php +++ b/tests/include/lib/src/TcpStat.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -103,4 +103,4 @@ private static function fmtTcpState(array $states, $type) } return $ret; } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/ThreadManager.php b/tests/include/lib/src/ThreadManager.php new file mode 100644 index 00000000000..9fbf19771b6 --- /dev/null +++ b/tests/include/lib/src/ThreadManager.php @@ -0,0 +1,20 @@ +parentFunc)(); + } else { + ($this->childFunc)(...$args); + } + } +} diff --git a/tests/include/lib/src/WaitRef.php b/tests/include/lib/src/WaitRef.php index 90f897446bb..65beb48d1a8 100644 --- a/tests/include/lib/src/WaitRef.php +++ b/tests/include/lib/src/WaitRef.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -41,4 +41,4 @@ static function wait(WaitRef &$wr) $wr = null; Swoole\Coroutine::yield(); } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/responder/get.php b/tests/include/lib/src/responder/get.php index e88108a0fd5..9d4a99d40f1 100644 --- a/tests/include/lib/src/responder/get.php +++ b/tests/include/lib/src/responder/get.php @@ -1,74 +1,79 @@ + echo "Hello World!\n"; + echo "Hello World!"; + break; +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index a255ce6a236..b60911ba7e4 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -45,7 +45,7 @@ function swoole_color(string $content, int $color): string function skip(string $reason, bool $is_skip = true, int $color = SWOOLE_COLOR_YELLOW) { if ($is_skip) { - exit('skip ' . swoole_color($reason, $color)); + exit('skip ' . swoole_color($reason, $color) . "\n"); } } @@ -56,6 +56,13 @@ function skip_if_php_version_lower_than($require_version = '7.0') } } +function skip_if_php_version_ge($require_version = '7.0') +{ + if (version_compare(PHP_VERSION, $require_version, '>=')) { + skip('need php version >= ' . $require_version); + } +} + function skip_if_php_version_between($a, $b) { if (version_compare(PHP_VERSION, $a, '>=') && version_compare(PHP_VERSION, $b, '<=')) { @@ -76,7 +83,14 @@ function skip_if_ini_bool_equal_to(string $name, bool $value) } } -function skip_if_constant_not_defined(string $constant_name) +function skip_if_no_nghttp(): void +{ + if (!str_contains(`nghttp --version 2>&1`, 'nghttp2')) { + skip('no nghttp'); + } +} + +function skip_if_constant_not_defined(string $constant_name): void { require_once __DIR__ . '/config.php'; skip("{$constant_name} is not defined", !defined($constant_name)); @@ -147,6 +161,16 @@ function skip_if_darwin() skip('not support on darwin', stripos(PHP_OS, 'Darwin') !== false); } +function skip_if_nts() +{ + skip('not support in nts', !defined('SWOOLE_THREAD')); +} + +function skip_if_not_linux() +{ + skip('only support linux', PHP_OS !== 'Linux'); +} + function skip_if_musl_libc() { skip('not support when use musl libc', !empty(`ldd 2>&1 | grep -i musl`)); @@ -162,9 +186,9 @@ function skip_if_in_valgrind(string $reason = 'valgrind is too slow') skip($reason, getenv('USE_ZEND_ALLOC') === '0'); } -function skip_if_in_travis(string $reason = 'not support in travis') +function skip_if_in_ci(string $reason = 'not support in CI') { - skip($reason, file_exists('/.travisenv')); + skip($reason, file_exists('/.cienv')); } function skip_if_in_docker(string $reason = 'not support in docker') @@ -257,3 +281,23 @@ function skip_unsupported(string $message = '') { skip($message ?: 'the test cannot continue to work for some implementation reasons'); } + +function skip_if_no_coroutine_get_execute_time() +{ + skip('no Swoole\Coroutine::getExecuteTime', !method_exists(Swoole\Coroutine::class, 'getExecuteTime')); +} + +function skip_if_no_ftp() +{ + require_once __DIR__ . '/config.php'; + skip('no available proxy', !check_tcp_port(FTP_HOST, FTP_PORT)); +} + +function skip_if_not_root() +{ + skip('not root user', posix_geteuid() !== 0); +} + +function skip_if_no_iouring() { + skip('no io-uring supports', !defined('SWOOLE_IOURING_DEFAULT')); +} diff --git a/tests/init b/tests/init index 75370e63f4a..0b730ce7063 100755 --- a/tests/init +++ b/tests/init @@ -36,17 +36,16 @@ function read_sql_file(string $file) } require __DIR__ . '/include/config.php'; +require __DIR__ . '/swoole_pdo_pgsql/pdo_pgsql.inc'; Swoole\Coroutine\run(function () { echo "[DB-init] initialization MySQL database...\n"; - $mysql = new Swoole\Coroutine\MySQL(); - $connected = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $connected = $mysql->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT); if (!$connected) { echo "[DB-init] Connect failed! Error#{$mysql->connect_errno}: {$mysql->connect_error}\n"; exit(1); @@ -58,5 +57,29 @@ Swoole\Coroutine\run(function () { exit(1); } } - echo "[DB-init] Done!\n"; + echo "[DB-init] MySQL Done!\n"; + + echo "[DB-init] initialization ODBC...\n"; + echo `set -ex`; + + file_put_contents('/etc/odbcinst.ini', "[mysql]" . PHP_EOL + . "Driver=libmaodbc.so" . PHP_EOL + . "Description=MariaDB Connector/ODBC(Unicode)" . PHP_EOL + . "UsageCount=1" . PHP_EOL + ); + echo `odbcinst -q -d -n "mysql"`; + + file_put_contents('/etc/odbc.ini', "[mysql-test]" . PHP_EOL + . "Description = MySQL test database" . PHP_EOL + . "Trace = On" . PHP_EOL + . "TraceFile = stderr" . PHP_EOL + . "Driver = mysql" . PHP_EOL + . "SERVER = " . MYSQL_SERVER_HOST . PHP_EOL + . "USER = " . MYSQL_SERVER_USER . PHP_EOL + . "PASSWORD =" . MYSQL_SERVER_PWD . PHP_EOL + . "PORT = " . MYSQL_SERVER_PORT . PHP_EOL + . "DATABASE = " . MYSQL_SERVER_DB); + echo `odbcinst -i -d -f /etc/odbc.ini`; + + echo "[DB-init] ODBC Done!\n"; }); diff --git a/tests/pgsql.sql b/tests/pgsql.sql new file mode 100644 index 00000000000..d9c2531ccbc --- /dev/null +++ b/tests/pgsql.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS weather; +CREATE TABLE weather ( + id SERIAL primary key NOT NULL, + city character varying(80), + temp_lo integer, + temp_hi integer, + prcp real, + date date); +INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id; +INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Test2', 11, 22, 0.3, '1994-11-28') RETURNING id; + +DROP TABLE IF EXISTS oid; +CREATE TABLE oid ( + id SERIAL primary key NOT NULL, + oid oid); diff --git a/tests/php-cs-fixer b/tests/php-cs-fixer new file mode 100755 index 00000000000..691599580f9 --- /dev/null +++ b/tests/php-cs-fixer @@ -0,0 +1,4 @@ +#!/usr/bin/env sh + +include/lib/vendor/friendsofphp/php-cs-fixer/php-cs-fixer "$@" + diff --git a/tests/run-tests b/tests/run-tests index 255668bf0ac..8f710753b87 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -31,6 +31,105 @@ * Minimum required PHP version: 7.0.0 */ +function show_usage(): void +{ + echo << Run up to simultaneous testing processes in parallel for + quicker testing on systems with multiple logical processors. + Note that this is experimental feature. + + -l Read the testfiles to be executed from . After the test + has finished all failed tests are written to the same . + If the list is empty and no further test is specified then + all tests are executed (same as: -r -w ). + + -r Read the testfiles to be executed from . + + -w Write a list of all failed tests to . + + -a Same as -w but append rather then truncating . + + -W Write a list of all tests and their result status to . + + -c Look for php.ini in directory or use as ini. + + -n Pass -n option to the php binary (Do not use a php.ini). + + -d foo=bar Pass -d option to the php binary (Define INI entry foo + with value 'bar'). + + -g Comma separated list of groups to show during test run + (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT). + + -m Test for memory leaks with Valgrind (equivalent to -M memcheck). + + -M Test for errors with Valgrind tool. + + -p Specify PHP executable to run. + + -P Use PHP_BINARY as PHP executable to run (default). + + -q Quiet, no user interaction (same as environment NO_INTERACTION). + + -s Write output to . + + -x Sets 'SKIP_SLOW_TESTS' environmental variable. + + --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. + + --verbose + -v Verbose mode. + + --help + -h This Help. + + --temp-source --temp-target [--temp-urlbase ] + Write temporary files to by replacing from the + filenames to generate with . In general you want to make + the path to your source files and some patch in + your web page hierarchy with pointing to . + + --keep-[all|php|skip|clean] + Do not delete 'all' files, 'php' test file, 'skip' or 'clean' + file. + + --set-timeout [n] + Set timeout for individual tests, where [n] is the number of + seconds. The default value is 60 seconds, or 300 seconds when + testing for memory leaks. + + --context [n] + Sets the number of lines of surrounding context to print for diffs. + The default value is 3. + + --show-[all|php|skip|clean|exp|diff|out|mem] + Show 'all' files, 'php' test file, 'skip' or 'clean' file. You + can also use this to show the output 'out', the expected result + 'exp', the difference between them 'diff' or the valgrind log + 'mem'. The result types get written independent of the log format, + however 'diff' only exists when a test fails. + + --show-slow [n] + Show all tests that took longer than [n] milliseconds to run. + + --no-clean Do not execute clean section if any. + + --color + --no-color Do/Don't colorize the result type in the test result. + + --repeat [n] + Run the tests multiple times in the same process and check the + output of the last execution (CLI SAPI only). + + --bless Bless failed tests using scripts/dev/bless_tests.php. + +HELP; +} + /** * One function to rule them all, one function to find them, one function to * bring them all and in the darkness bind them. @@ -57,7 +156,7 @@ function main() // Parallel testing global $workers, $workerID; - define('IS_WINDOWS', substr(PHP_OS, 0, 3) == "WIN"); + define('IS_WINDOWS', str_starts_with(PHP_OS, "WIN")); $workerID = 0; if (getenv("TEST_PHP_WORKER")) { @@ -84,7 +183,7 @@ function main() +-----------------------------------------------------------+ NO_PROC_OPEN_ERROR; - exit(1); + exit(200); } // If timezone is not set, use UTC. @@ -232,18 +331,18 @@ NO_PROC_OPEN_ERROR; if (getenv('SHOW_ONLY_GROUPS')) { $SHOW_ONLY_GROUPS = explode(",", getenv('SHOW_ONLY_GROUPS')); } else { - $SHOW_ONLY_GROUPS = array(); + $SHOW_ONLY_GROUPS = []; } // Check whether user test dirs are requested. if (getenv('TEST_PHP_USER')) { $user_tests = explode(',', getenv('TEST_PHP_USER')); } else { - $user_tests = array(); + $user_tests = []; } - $exts_to_test = array(); - $ini_overwrites = array( + $exts_to_test = []; + $ini_overwrites = [ 'output_handler=', 'open_basedir=', 'disable_functions=', @@ -265,36 +364,42 @@ NO_PROC_OPEN_ERROR; 'ignore_repeated_errors=0', 'precision=14', 'serialize_precision=-1', - 'memory_limit=128M', + 'memory_limit=1024M', 'log_errors_max_len=0', 'opcache.fast_shutdown=0', 'opcache.file_update_protection=0', 'opcache.revalidate_freq=0', + 'opcache.jit_hot_loop=1', + 'opcache.jit_hot_func=1', + 'opcache.jit_hot_return=1', + 'opcache.jit_hot_side_exit=1', 'zend.assertions=1', 'zend.exception_ignore_args=0', - ); + 'zend.exception_string_param_max_len=15', + 'short_open_tag=0', + ]; $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0'; define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); define('QA_REPORTS_PAGE', 'http://qa.php.net/reports'); - define('TRAVIS_CI', (bool)getenv('TRAVIS')); + define('TRAVIS_CI', (bool) getenv('TRAVIS')); // Determine the tests to be run. - $test_files = array(); - $redir_tests = array(); - $test_results = array(); - $PHP_FAILED_TESTS = array( - 'BORKED' => array(), - 'FAILED' => array(), - 'WARNED' => array(), - 'LEAKED' => array(), - 'XFAILED' => array(), - 'XLEAKED' => array(), - 'SLOW' => array() - ); + $test_files = []; + $redir_tests = []; + $test_results = []; + $PHP_FAILED_TESTS = [ + 'BORKED' => [], + 'FAILED' => [], + 'WARNED' => [], + 'LEAKED' => [], + 'XFAILED' => [], + 'XLEAKED' => [], + 'SLOW' => [] + ]; // If parameters given assume they represent selected tests to run. $result_tests_file = false; @@ -318,12 +423,12 @@ NO_PROC_OPEN_ERROR; $shuffle = false; $workers = null; - $cfgtypes = array('show', 'keep'); - $cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'); - $cfg = array(); + $cfgtypes = ['show', 'keep']; + $cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem']; + $cfg = []; foreach ($cfgtypes as $type) { - $cfg[$type] = array(); + $cfg[$type] = []; foreach ($cfgfiles as $file) { $cfg[$type][$file] = false; @@ -331,7 +436,7 @@ NO_PROC_OPEN_ERROR; } if (!isset($argc, $argv) || !$argc) { - $argv = array(__FILE__); + $argv = [__FILE__]; $argc = 1; } @@ -352,7 +457,6 @@ NO_PROC_OPEN_ERROR; $repeat = substr($argv[$i], 0, 1) == '-'; while ($repeat) { - if (!$is_switch) { $switch = substr($argv[$i], 1, 1); } @@ -397,9 +501,9 @@ NO_PROC_OPEN_ERROR; $test_list = file($argv[++$i]); if ($test_list) { foreach ($test_list as $test) { - $matches = array(); + $matches = []; if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { - $redir_tests[] = array($matches[1], $matches[2]); + $redir_tests[] = [$matches[1], $matches[2]]; } else { if (strlen($test)) { $test_files[] = trim($test); @@ -411,7 +515,7 @@ NO_PROC_OPEN_ERROR; break; } $i--; - // break left intentionally + // no break case 'w': $failed_tests_file = fopen($argv[++$i], 'w+t'); break; @@ -539,100 +643,15 @@ NO_PROC_OPEN_ERROR; break; case '--version': echo '$Id: ebcaabba02a5af6f5dc62dda027befb95385d5fc $' . "\n"; - exit(1); + exit(2); default: echo "Illegal switch '$switch' specified!\n"; case 'h': case '-help': case '--help': - echo << Run up to simultaneous testing processes in parallel for - quicker testing on systems with multiple logical processors. - Note that this is experimental feature. - - -l Read the testfiles to be executed from . After the test - has finished all failed tests are written to the same . - If the list is empty and no further test is specified then - all tests are executed (same as: -r -w ). - - -r Read the testfiles to be executed from . - - -w Write a list of all failed tests to . - - -a Same as -w but append rather then truncating . - - -W Write a list of all tests and their result status to . - - -c Look for php.ini in directory or use as ini. - - -n Pass -n option to the php binary (Do not use a php.ini). - - -d foo=bar Pass -d option to the php binary (Define INI entry foo - with value 'bar'). - - -g Comma separated list of groups to show during test run - (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT). - - -m Test for memory leaks with Valgrind (equivalent to -M memcheck). - - -M Test for errors with Valgrind tool. - - -p Specify PHP executable to run. - - -P Use PHP_BINARY as PHP executable to run (default). - - -q Quiet, no user interaction (same as environment NO_INTERACTION). - - -s Write output to . - - -x Sets 'SKIP_SLOW_TESTS' environmental variable. - - --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. - - --verbose - -v Verbose mode. - - --help - -h This Help. - - --html Generate HTML output. - - --temp-source --temp-target [--temp-urlbase ] - Write temporary files to by replacing from the - filenames to generate with . If --html is being used and - given then the generated links are relative and prefixed - with the given url. In general you want to make the path - to your source files and some patch in your web page - hierarchy with pointing to . - - --keep-[all|php|skip|clean] - Do not delete 'all' files, 'php' test file, 'skip' or 'clean' - file. - - --set-timeout [n] - Set timeout for individual tests, where [n] is the number of - seconds. The default value is 60 seconds, or 300 seconds when - testing for memory leaks. - - --show-[all|php|skip|clean|exp|diff|out|mem] - Show 'all' files, 'php' test file, 'skip' or 'clean' file. You - can also use this to show the output 'out', the expected result - 'exp', the difference between them 'diff' or the valgrind log - 'mem'. The result types get written independent of the log format, - however 'diff' only exists when a test fails. - - --show-slow [n] - Show all tests that took longer than [n] milliseconds to run. - - --no-clean Do not execute clean section if any. - -HELP; - exit(1); + show_usage(); + exit(2); } } @@ -676,7 +695,7 @@ HELP; $environment['TEST_PHP_EXECUTABLE'] = $php; } - if (strlen($conf_passed)) { + if ($conf_passed && strlen($conf_passed)) { if (IS_WINDOWS) { $pass_options .= " -c " . escapeshellarg($conf_passed); } else { @@ -738,7 +757,7 @@ HELP; if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { - exit(1); + exit(201); } return; @@ -815,7 +834,7 @@ HELP; junit_save_xml(); if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['LEAKED'])) { - exit(1); + exit(202); } exit(0); } @@ -834,6 +853,11 @@ if (!function_exists("hrtime")) { } } +function get_shortname($file) +{ + return str_replace(TEST_PHP_SRCDIR . '/', '', $file); +} + function verify_config() { global $php; @@ -981,8 +1005,8 @@ function save_or_mail_results() if ($sum_results['FAILED']) { foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output'])); + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff'])); $failed_tests_data .= $sep . "\n\n"; } $status = "failed"; @@ -1178,12 +1202,12 @@ function save_text($filename, $text, $filename_copy = null) global $DETAILED; if ($filename_copy && $filename_copy != $filename) { - if (file_put_contents($filename_copy, $text, FILE_BINARY) === false) { + if (file_put_contents($filename_copy, $text) === false) { error("Cannot open file '" . $filename_copy . "' (save_text)"); } } - if (file_put_contents($filename, $text, FILE_BINARY) === false) { + if (file_put_contents($filename, $text) === false) { error("Cannot open file '" . $filename . "' (save_text)"); } @@ -1291,6 +1315,12 @@ function system_with_timeout($commandline, $env = null, $stdin = null, $captureS break; } $data .= $line; + if (strlen($data) > SWOOLE_TEST_OUTPUT_MAX_SIZE) { + echo "FATAL ERROR: The output content exceeds the maximum size\n"; + echo "========================================================\n"; + echo substr($data, 0, 65536); + exit(253); + } } } @@ -1333,6 +1363,13 @@ function run_all_tests($test_files, $env, $redir_tested = null) } $test_idx++; + if ($workerID) { + send_message($workerSock, [ + "type" => "begin", + "file" => $name, + ]); + } + if ($workerID) { $PHP_FAILED_TESTS = ['BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => []]; ob_start(); @@ -1541,7 +1578,7 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { $rawMessage = $rawMessageBuffers[$i] . $rawMessage; $rawMessageBuffers[$i] = ''; } - if (substr($rawMessage, -1) !== "\n") { + if (!str_ends_with($rawMessage, "\n")) { $rawMessageBuffers[$i] = $rawMessage; continue; } @@ -1615,6 +1652,11 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { goto escape; } break; + case "begin": + if (!$SHOW_ONLY_GROUPS) { + show_test($test_idx, get_shortname($message['file'])); + } + break; case "test_result": list($name, $index, $result, $resultText) = [$message["name"], $message["index"], $message["result"], $message["text"]]; foreach ($message["PHP_FAILED_TESTS"] as $category => $tests) { @@ -1628,10 +1670,6 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { echo $resultText; - if (!$SHOW_ONLY_GROUPS) { - show_test($test_idx, count($workerProcs) . "/$workers concurrent test workers running"); - } - if (!is_array($name) && $result != 'REDIR') { $test_results[$index] = $result; @@ -1921,7 +1959,7 @@ TEST $file $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key])); if (file_exists($section_text[$key])) { - $section_text[$prefix] = file_get_contents($section_text[$key], FILE_BINARY); + $section_text[$prefix] = file_get_contents($section_text[$key]); unset($section_text[$key]); } else { $bork_info = "could not load --" . $key . "-- " . dirname($file) . '/' . trim($section_text[$key]); @@ -1936,7 +1974,7 @@ TEST $file } fclose($fp); - $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); + $shortname = get_shortname($file); $tested_file = $shortname; if ($bork_info !== null) { @@ -2018,6 +2056,7 @@ TEST $file if (!$SHOW_ONLY_GROUPS && !$workerID) { show_test($test_idx, $shortname); + echo PHP_EOL; } if (is_array($IN_REDIRECT)) { @@ -2760,12 +2799,12 @@ COMMAND $cmd if (!$passed) { // write .exp - if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { + if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted) === false) { error("Cannot create expected test output - $exp_filename"); } // write .out - if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { + if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output) === false) { error("Cannot create test output - $output_filename"); } @@ -2783,7 +2822,7 @@ COMMAND $cmd @`lsof -i:{$port} | grep LISTEN | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1`; } - if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { + if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff) === false) { error("Cannot create test diff - $diff_filename"); } @@ -2791,7 +2830,7 @@ COMMAND $cmd if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh {$cmd} -", FILE_BINARY) === false) { +") === false) { error("Cannot create test shell script - $sh_filename"); } chmod($sh_filename, 0755); @@ -2803,7 +2842,7 @@ $wanted ---- ACTUAL OUTPUT $output ---- FAILED -", FILE_BINARY) === false) { +") === false) { error("Cannot create test log - $log_filename"); error_report($file, $log_filename, $tested); } @@ -2967,7 +3006,7 @@ function generate_diff($wanted, $wanted_re, $output) function error($message) { echo "ERROR: {$message}\n"; - exit(1); + exit(240); } function settings2array($settings, &$ini_settings) @@ -3295,7 +3334,7 @@ function show_test($test_idx, $shortname) global $line_length; // swoole patch: pretty output - $str = "TEST $test_idx/$test_cnt "; + $str = "TEST $test_idx/$test_cnt [$shortname]"; $line_length = strlen($str); echo $str; flush(); @@ -3308,7 +3347,7 @@ function clear_show_test() { if (!$workerID) { // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", $line_length), "\r"; + echo str_repeat(" ", intval($line_length)), "\r"; } } @@ -3742,7 +3781,7 @@ class RuntestsValgrind } // swoole patch: color const -define('SWOOLE_TEST_TIMEOUT', file_exists('/.travisenv') ? 10 : 30); +define('SWOOLE_TEST_TIMEOUT', file_exists('/.cienv') ? 10 : 30); define('SWOOLE_COLOR_RED', 1); define('SWOOLE_COLOR_GREEN', 2); define('SWOOLE_COLOR_YELLOW', 3); @@ -3750,6 +3789,7 @@ define('SWOOLE_COLOR_BLUE', 4); define('SWOOLE_COLOR_MAGENTA', 5); define('SWOOLE_COLOR_CYAN', 6); define('SWOOLE_COLOR_WHITE', 7); +define('SWOOLE_TEST_OUTPUT_MAX_SIZE', 64 * 1024 * 1024); // swoole patch: libc check define('SWOOLE_IS_MUSL_LIBC', !empty(`ldd 2>&1 | grep -i musl`)); diff --git a/tests/start.sh b/tests/start.sh index 7ec1efb9137..bd636afda4c 100755 --- a/tests/start.sh +++ b/tests/start.sh @@ -29,7 +29,6 @@ else swoole_process \ swoole_process_pool \ swoole_table \ - \ swoole_coroutine* \ swoole_channel_coro \ swoole_client_coro \ @@ -39,8 +38,6 @@ else swoole_http_server \ swoole_websocket_server \ swoole_redis_server \ - swoole_mysql_coro \ - swoole_redis_coro \ swoole_socket_coro \ swoole_runtime" if [ ${#} -gt 1 ]; then @@ -50,6 +47,10 @@ else fi else glob="$@" + if [ $(expr substr "$glob" 1 6) = "tests/" ]; then + # 去掉 tests/ 前缀 + glob="${glob#tests/}" + fi fi fi diff --git a/tests/swoole_atomic/atomic.phpt b/tests/swoole_atomic/atomic.phpt index 7496f63e8c6..939e5e905a6 100644 --- a/tests/swoole_atomic/atomic.phpt +++ b/tests/swoole_atomic/atomic.phpt @@ -6,7 +6,7 @@ swoole_atomic: add/sub/get/cmpset add(199), 200); Assert::same($atomic->sub(35), 165); diff --git a/tests/swoole_atomic/dtor_in_child.phpt b/tests/swoole_atomic/dtor_in_child.phpt index 3138a716dec..c2ba8f8b557 100644 --- a/tests/swoole_atomic/dtor_in_child.phpt +++ b/tests/swoole_atomic/dtor_in_child.phpt @@ -6,9 +6,9 @@ swoole_atomic: destruct objects in child processe wait(); echo "Child OK\n"; exit(0); @@ -18,7 +18,7 @@ $p->start(); usleep(200000); echo "Master OK\n"; $atomic->wakeup(1); -$status = swoole_process::wait(); +$status = Swoole\Process::wait(); ?> --EXPECT-- Master OK diff --git a/tests/swoole_atomic/wait.phpt b/tests/swoole_atomic/wait.phpt index 8ab58924e4b..ebaf3567395 100644 --- a/tests/swoole_atomic/wait.phpt +++ b/tests/swoole_atomic/wait.phpt @@ -6,14 +6,14 @@ swoole_atomic: wakeup & wait wait(); echo "Child OK\n"; @@ -28,7 +28,7 @@ $atomic->wakeup(N); for ($i = 0; $i < 4; $i++) { - $status = swoole_process::wait(); + $status = Swoole\Process::wait(); } ?> --EXPECT-- diff --git a/tests/swoole_atomic/wait_ex.phpt b/tests/swoole_atomic/wait_ex.phpt index f8a0f548b04..ff5048c5472 100644 --- a/tests/swoole_atomic/wait_ex.phpt +++ b/tests/swoole_atomic/wait_ex.phpt @@ -6,7 +6,7 @@ swoole_atomic: wakeup & wait ex set(['worker_num' => 4, 'log_file' => '/dev/null']); $server->on('WorkerStart', function (Swoole\Http\Server $server, int $wid) use ($atomic) { if ($wid === 0) { diff --git a/tests/swoole_channel_coro/10.phpt b/tests/swoole_channel_coro/10.phpt index f9d397088a9..b744fe56e1d 100644 --- a/tests/swoole_channel_coro/10.phpt +++ b/tests/swoole_channel_coro/10.phpt @@ -16,6 +16,6 @@ go(function () { Assert::false($chan->pop(0.001)); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/2.phpt b/tests/swoole_channel_coro/2.phpt index e599a9b21ab..89b7387c89a 100644 --- a/tests/swoole_channel_coro/2.phpt +++ b/tests/swoole_channel_coro/2.phpt @@ -28,7 +28,7 @@ go(function () use ($c1,$num) { }); echo "main end\n"; -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- pop start diff --git a/tests/swoole_channel_coro/4.phpt b/tests/swoole_channel_coro/4.phpt index c919b0d2a75..f5168860fda 100644 --- a/tests/swoole_channel_coro/4.phpt +++ b/tests/swoole_channel_coro/4.phpt @@ -25,7 +25,7 @@ go(function () use ($c1, $num) { } }); echo "main end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- push start diff --git a/tests/swoole_channel_coro/6.phpt b/tests/swoole_channel_coro/6.phpt index 863c0210d41..ea5e8880c08 100644 --- a/tests/swoole_channel_coro/6.phpt +++ b/tests/swoole_channel_coro/6.phpt @@ -31,7 +31,7 @@ go(function () use ($chan){ }); echo 'master end' . PHP_EOL; -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- coro1 start diff --git a/tests/swoole_channel_coro/7.phpt b/tests/swoole_channel_coro/7.phpt index 9de554af8cb..24f4e3bf59f 100644 --- a/tests/swoole_channel_coro/7.phpt +++ b/tests/swoole_channel_coro/7.phpt @@ -26,7 +26,7 @@ go(function ()use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- int(0) diff --git a/tests/swoole_channel_coro/8.phpt b/tests/swoole_channel_coro/8.phpt index a447724bce2..d4e84027dc2 100644 --- a/tests/swoole_channel_coro/8.phpt +++ b/tests/swoole_channel_coro/8.phpt @@ -16,7 +16,7 @@ for ($i = 0; $i < $n; $i++) { }); }; -swoole_timer_after(500, function () use ($chan) { +Swoole\Timer::after(500, function () use ($chan) { for ($i = 0; $i < 6; $i++) { $chan->push($i); } @@ -31,7 +31,7 @@ go(function ()use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- [pop]0 diff --git a/tests/swoole_channel_coro/9.phpt b/tests/swoole_channel_coro/9.phpt index 45bc67eabef..d4e1e22a7e1 100644 --- a/tests/swoole_channel_coro/9.phpt +++ b/tests/swoole_channel_coro/9.phpt @@ -15,7 +15,7 @@ for ($i = 0; $i < 4; $i++) { }); }; -swoole_timer_after(200, function () use ($chan) { +Swoole\Timer::after(200, function () use ($chan) { for ($i = 0; $i < 6; $i++) { $chan->push($i); } @@ -30,7 +30,7 @@ go(function () use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- [read]0 diff --git a/tests/swoole_channel_coro/basic.phpt b/tests/swoole_channel_coro/basic.phpt index 0a5a02c1759..7f10066e0f7 100644 --- a/tests/swoole_channel_coro/basic.phpt +++ b/tests/swoole_channel_coro/basic.phpt @@ -22,7 +22,7 @@ co::create(function () use ($chan) { } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/benchmark.phpt b/tests/swoole_channel_coro/benchmark.phpt index e9161ad977d..9197152eee6 100644 --- a/tests/swoole_channel_coro/benchmark.phpt +++ b/tests/swoole_channel_coro/benchmark.phpt @@ -52,7 +52,7 @@ go(function () use (&$time, $chan) { $chan->close(); }); -swoole_event_wait(); +Swoole\Event::wait(); var_dump($time); $diff = $time['channel_raw'] - $time['splQueue']; var_dump($diff); diff --git a/tests/swoole_channel_coro/blocking_timeout.phpt b/tests/swoole_channel_coro/blocking_timeout.phpt index 6fe3d6de3b7..f096a5a45cd 100644 --- a/tests/swoole_channel_coro/blocking_timeout.phpt +++ b/tests/swoole_channel_coro/blocking_timeout.phpt @@ -22,6 +22,6 @@ go(function () use ($chan) { $chan->push(999955); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/chan_select_timeout.phpt b/tests/swoole_channel_coro/chan_select_timeout.phpt index b0b25a7f438..d0f2fdc8dcf 100644 --- a/tests/swoole_channel_coro/chan_select_timeout.phpt +++ b/tests/swoole_channel_coro/chan_select_timeout.phpt @@ -19,6 +19,6 @@ go(function () use ($chan) { Assert::false($result); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/chan_stats.phpt b/tests/swoole_channel_coro/chan_stats.phpt index 1551e2d38d1..af266783f48 100644 --- a/tests/swoole_channel_coro/chan_stats.phpt +++ b/tests/swoole_channel_coro/chan_stats.phpt @@ -22,6 +22,6 @@ go(function () use ($chan) { Assert::same($chan->stats()['queue_num'], 0); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/close.phpt b/tests/swoole_channel_coro/close.phpt index 4c390188bf9..70f2d5c3139 100644 --- a/tests/swoole_channel_coro/close.phpt +++ b/tests/swoole_channel_coro/close.phpt @@ -32,7 +32,7 @@ go(function () { unset($a['1']); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- chan get data :11 diff --git a/tests/swoole_channel_coro/hybird_chan3.phpt b/tests/swoole_channel_coro/hybird_chan3.phpt index 94fddda37a0..0fe3eb149de 100644 --- a/tests/swoole_channel_coro/hybird_chan3.phpt +++ b/tests/swoole_channel_coro/hybird_chan3.phpt @@ -26,7 +26,7 @@ for ($i=0; $i < $coro_num; $i++) { } }); } -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- @0->coro:%d pop chan id :%d diff --git a/tests/swoole_channel_coro/lock.phpt b/tests/swoole_channel_coro/lock.phpt index 8ff3d515e6e..c15d1fede3a 100644 --- a/tests/swoole_channel_coro/lock.phpt +++ b/tests/swoole_channel_coro/lock.phpt @@ -57,7 +57,7 @@ go(function () { } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- fail diff --git a/tests/swoole_channel_coro/no_ctor.phpt b/tests/swoole_channel_coro/no_ctor.phpt index 1e497c6a4c9..1404f9065da 100644 --- a/tests/swoole_channel_coro/no_ctor.phpt +++ b/tests/swoole_channel_coro/no_ctor.phpt @@ -6,17 +6,21 @@ swoole_channel_coro: no ctor pop(); +$pm = ProcessManager::exec(function () { + go(function () { + $chan = new MyChan(100); + $chan->pop(); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> ---EXPECTF-- -Fatal error: Swoole\Coroutine\Channel::pop(): you must call Channel constructor first in %s on line %d +--EXPECT-- diff --git a/tests/swoole_channel_coro/pool.phpt b/tests/swoole_channel_coro/pool.phpt index de0e7dbcc79..e7622f05944 100644 --- a/tests/swoole_channel_coro/pool.phpt +++ b/tests/swoole_channel_coro/pool.phpt @@ -19,7 +19,7 @@ class RedisPool { $this->pool = new \Swoole\Coroutine\Channel($size); for ($i = 0; $i < $size; $i++) { - $redis = new Swoole\Coroutine\Redis(); + $redis = new \redis(); $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); if ($res == false) { throw new \RuntimeException("failed to connect redis server."); @@ -29,12 +29,12 @@ class RedisPool } } - public function get(): \Swoole\Coroutine\Redis + public function get(): \redis { return $this->pool->pop(); } - public function put(\Swoole\Coroutine\Redis $redis) + public function put(\redis $redis) { $this->pool->push($redis); } @@ -47,6 +47,7 @@ class RedisPool } $count = 0; +\Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () { $pool = new RedisPool(); // max concurrency num is more than max connections @@ -69,7 +70,7 @@ go(function () { } }); -swoole_event_wait(); +Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_MID * MAX_REQUESTS); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/pop_timeout3.phpt b/tests/swoole_channel_coro/pop_timeout3.phpt index 52f7a414804..8a11e68b077 100644 --- a/tests/swoole_channel_coro/pop_timeout3.phpt +++ b/tests/swoole_channel_coro/pop_timeout3.phpt @@ -18,7 +18,7 @@ go(function () use ($c1) { $ret = $c1->push("chan-1"); echo "chan push ret:".var_export($ret,1)." error:".$c1->errCode."\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- pop ret:'chan-1' error:0 diff --git a/tests/swoole_channel_coro/pop_timeout7.phpt b/tests/swoole_channel_coro/pop_timeout7.phpt index f1e039dd80b..30bfa6b744e 100644 --- a/tests/swoole_channel_coro/pop_timeout7.phpt +++ b/tests/swoole_channel_coro/pop_timeout7.phpt @@ -23,7 +23,7 @@ go(function () use ($c1) { $ret = $c1->push("chan-1"); echo "chan push ret:".var_export($ret,1)."\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- pop ret:false error:-1 diff --git a/tests/swoole_channel_coro/push_timeout1.phpt b/tests/swoole_channel_coro/push_timeout1.phpt index 25bc9ced70a..d492b4c396b 100644 --- a/tests/swoole_channel_coro/push_timeout1.phpt +++ b/tests/swoole_channel_coro/push_timeout1.phpt @@ -23,7 +23,7 @@ go(function () use ($channel) { $ret = $channel->pop(); Assert::same($ret, 'foo'); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout2.phpt b/tests/swoole_channel_coro/push_timeout2.phpt index e36b148d321..7bdd8da6eb6 100644 --- a/tests/swoole_channel_coro/push_timeout2.phpt +++ b/tests/swoole_channel_coro/push_timeout2.phpt @@ -20,7 +20,7 @@ go(function () use ($chan) { Assert::assert($chan->push('swoole', 1)); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout3.phpt b/tests/swoole_channel_coro/push_timeout3.phpt index 0a1085ff540..9001cdbdeff 100644 --- a/tests/swoole_channel_coro/push_timeout3.phpt +++ b/tests/swoole_channel_coro/push_timeout3.phpt @@ -13,7 +13,7 @@ go(function () use ($channel) { Assert::assert(!$channel->push(1, 0.1)); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout4.phpt b/tests/swoole_channel_coro/push_timeout4.phpt index 2bc0193f975..abe99dbc756 100644 --- a/tests/swoole_channel_coro/push_timeout4.phpt +++ b/tests/swoole_channel_coro/push_timeout4.phpt @@ -17,7 +17,7 @@ go(function () use ($channel) { Assert::same($channel->pop(0.1), 1); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/type.phpt b/tests/swoole_channel_coro/type.phpt index 428a66f28b6..3dc32273568 100644 --- a/tests/swoole_channel_coro/type.phpt +++ b/tests/swoole_channel_coro/type.phpt @@ -37,7 +37,7 @@ go(function () use ($chan, $exit_status_list) }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_client_async/base.phpt b/tests/swoole_client_async/base.phpt new file mode 100644 index 00000000000..d293056b12c --- /dev/null +++ b/tests/swoole_client_async/base.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client connect & send & close +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $cli->send(RandStr::gen(1024, RandStr::ALL)); +}); + +$cli->on("receive", function(Swoole\Async\Client $cli, $data){ + $recv_len = strlen($data); + // print("receive: len $recv_len"); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + $cli->close(); + Assert::false($cli->isConnected()); +}); + +$cli->on("error", function(Swoole\Async\Client $cli) { + print("error"); +}); + +$cli->on("close", function(Swoole\Async\Client $cli) { + Swoole\Event::exit(); + echo "SUCCESS"; +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 0.2); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/big_package_memory_leak.phpt b/tests/swoole_client_async/big_package_memory_leak.phpt new file mode 100644 index 00000000000..a12d4b91b72 --- /dev/null +++ b/tests/swoole_client_async/big_package_memory_leak.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_client_async: big_package_memory_leak +--SKIPIF-- + +--FILE-- +set(['socket_buffer_size' => 2 * 1024 * 1024]); +$cli->on("connect", function (Swoole\Async\Client $cli) { + $cli->send(str_repeat("\0", 1024 * 1024 * 1.9)); +}); +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + $cli->send($data); +}); +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; +}); +$cli->on("close", function (Swoole\Async\Client $cli) use ($closeServer) { + echo "closed\n"; + $closeServer(); +}); +$cli->connect('127.0.0.1', $port); +Assert::same(memory_get_usage(true), $mem); +echo "SUCCESS\n"; + +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS +closed diff --git a/tests/swoole_client_async/buffer_full.phpt b/tests/swoole_client_async/buffer_full.phpt new file mode 100644 index 00000000000..48495f71044 --- /dev/null +++ b/tests/swoole_client_async/buffer_full.phpt @@ -0,0 +1,91 @@ +--TEST-- +swoole_client_async: onBufferFull & onBufferEmpty +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + Co::set(['log_level' => 5, 'display_errors' => false]); + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(['socket_buffer_size' => 1 * 1024 * 1024,]); + $client->buffer = array(); + + $countBufferEmpty = 0; + $countBufferFull = 0; + + $client->on("connect", function (Client $cli) { + for ($i = 0; $i < 1024; $i++) { + $data = str_repeat('A', 8192); + if ($cli->send($data) === false and $cli->errCode == 1008) { + $cli->buffer[] = $data; + } + } + }); + + $client->on("receive", function (Client $cli, $data) { + $cli->send(pack('N', 8) . 'shutdown'); + $cli->close(); + Assert::same($data, md5_file(TEST_IMAGE)); + }); + + $client->on("error", function ($cli) { + echo "Connect failed\n"; + }); + + $client->on("close", function ($cli) { + + }); + + $client->on("bufferEmpty", function (Client $cli) use (&$countBufferEmpty) { + $countBufferEmpty++; + foreach ($cli->buffer as $k => $data) { + if ($cli->send($data) === false and $cli->errCode == 1008) { + break; + } else { + unset($cli->buffer[$k]); + } + } + if (count($cli->buffer) == 0) { + $cli->close(); + } + }); + + $client->on("bufferFull", function (Client $cli) use (&$countBufferFull) { + $countBufferFull++; + }); + + $client->connect(TCP_SERVER_HOST, $port, 0.5); + Swoole\Event::wait(); + + Assert::greaterThanEq($countBufferEmpty, 1); + Assert::greaterThanEq($countBufferFull, 1); +}; + +$pm->childFunc = function () use ($pm, $port) { + $socket = stream_socket_server("tcp://0.0.0.0:{$port}", $errno, $errstr) or die("$errstr ($errno)
\n"); + $pm->wakeup(); + while ($conn = stream_socket_accept($socket)) { + for ($i = 0; $i < 4; $i++) { + usleep(500000); + for ($j = 0; $j < 256; $j++) { + $data = fread($conn, 8192); + } + } + fclose($conn); + break; + } + fclose($socket); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- + diff --git a/tests/swoole_client_async/connect_dns.phpt b/tests/swoole_client_async/connect_dns.phpt new file mode 100644 index 00000000000..3abc712f69c --- /dev/null +++ b/tests/swoole_client_async/connect_dns.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_client_async: connect & dns +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $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"); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + Assert::assert(strlen($data) > 0); + $cli->close(); + Assert::false($cli->isConnected()); + echo "DONE\n"; +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS\n"; +}); + +$cli->connect("www.baidu.com", 80, 2.0); + +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS +DONE diff --git a/tests/swoole_client_async/connect_refuse.phpt b/tests/swoole_client_async/connect_refuse.phpt new file mode 100644 index 00000000000..431d586e771 --- /dev/null +++ b/tests/swoole_client_async/connect_refuse.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_async: connect refuse +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +$cli->connect('127.0.0.1', 65535); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_refuse_udg.phpt b/tests/swoole_client_async/connect_refuse_udg.phpt new file mode 100644 index 00000000000..72e3a3a60cb --- /dev/null +++ b/tests/swoole_client_async/connect_refuse_udg.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_client_async: connect refuse with unix dgram +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +@$cli->connect("/test.sock", 0, 0.5, 1); + +Swoole\Event::wait(); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_refuse_unix.phpt b/tests/swoole_client_async/connect_refuse_unix.phpt new file mode 100644 index 00000000000..1c26fa9e05f --- /dev/null +++ b/tests/swoole_client_async/connect_refuse_unix.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_client_async: connect refuse with unix stream +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +@$cli->connect("/test.sock"); + +Swoole\Event::wait(); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_timeout.phpt b/tests/swoole_client_async/connect_timeout.phpt new file mode 100644 index 00000000000..76a1341c98a --- /dev/null +++ b/tests/swoole_client_async/connect_timeout.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_client_async: connect_host_not_found +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { + echo "error\n"; +}); +$cli->on("close", function(Swoole\Async\Client $cli) { + echo "close\n"; +}); + +$cli->connect("192.0.0.1", 9000, 0.1); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_twice.phpt b/tests/swoole_client_async/connect_twice.phpt new file mode 100644 index 00000000000..78a77f0c642 --- /dev/null +++ b/tests/swoole_client_async/connect_twice.phpt @@ -0,0 +1,11 @@ +--TEST-- +swoole_client_async: connect twice +--SKIPIF-- + +--FILE-- + +--EXPECT-- +error diff --git a/tests/swoole_client_async/enableSSL.phpt b/tests/swoole_client_async/enableSSL.phpt new file mode 100644 index 00000000000..fcf1ab26f3d --- /dev/null +++ b/tests/swoole_client_async/enableSSL.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + echo 'connected' . PHP_EOL; + $cli->enableSSL(function ($cli) { + echo "SSL READY\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"); + }); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + Assert::assert(strlen($data) > 0); + Assert::contains($data, 'www.baidu.com'); + $cli->close(); + Assert::false($cli->isConnected()); + echo "DONE\n"; +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS\n"; +}); + +$cli->connect("www.baidu.com", 443, 2.0); + +Swoole\Event::wait(); +?> +--EXPECT-- +connected +SSL READY +SUCCESS +DONE diff --git a/tests/swoole_client_async/enableSSL_bad_callback.phpt b/tests/swoole_client_async/enableSSL_bad_callback.phpt new file mode 100644 index 00000000000..707430db181 --- /dev/null +++ b/tests/swoole_client_async/enableSSL_bad_callback.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_client_async: enableSSL with bad callback +--SKIPIF-- + +--FILE-- +enableSSL(); +} catch (Exception $e) { + Assert::contains($e->getMessage(), 'require `onSslReady` callback'); +} +?> +--EXPECTF-- diff --git a/tests/swoole_client_async/enableSSL_before_connect.phpt b/tests/swoole_client_async/enableSSL_before_connect.phpt new file mode 100644 index 00000000000..21d86b1873a --- /dev/null +++ b/tests/swoole_client_async/enableSSL_before_connect.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_client_async: enableSSL before connect +--SKIPIF-- + +--FILE-- +enableSSL(function ($cli) { + echo "SSL READY\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"); +}); +Assert::false($res); + +?> +--EXPECTF-- +Warning: Swoole\Async\Client::enableSSL(): client is not connected to server in %s on line %d diff --git a/tests/swoole_client_async/eof.phpt b/tests/swoole_client_async/eof.phpt new file mode 100644 index 00000000000..636450adbd2 --- /dev/null +++ b/tests/swoole_client_async/eof.phpt @@ -0,0 +1,111 @@ +--TEST-- +swoole_client_async: eof protocol [async] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set(['open_eof_check' => true, 'open_eof_split' => true, "package_eof" => "\r\n\r\n"]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + static $i = 0; + $i++; + + Assert::assert($pkg != false); + Assert::assert(str_ends_with($pkg, "\r\n\r\n")); + + //小包 + if ($i <= 1000) { + Assert::assert($pkg and strlen($pkg) <= 2048); + if ($i == 1000) { + echo "SUCCESS\n"; + } + } //慢速发送 + elseif ($i <= 1100) { + Assert::assert($pkg and strlen($pkg) <= 8192); + if ($i == 1100) { + echo "SUCCESS\n"; + } + } //大包 + else { + $_pkg = unserialize(substr($pkg, 0, strlen($pkg) - 4)); + Assert::assert(is_array($_pkg)); + Assert::same($_pkg['i'], $i - 1100 - 1); + Assert::same(md5($_pkg['data']), $_pkg['md5']); + Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024 * 1.5); + if ($i == 2100) { + echo "SUCCESS\n"; + $cli->close(); + Swoole\Process::kill($pid); + } + } + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + echo "CLOSE\n"; + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $port, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, + 'socket_buffer_size' => 128 * 1024 * 1024, + 'worker_num' => 1, + 'log_file' => TEST_LOG_FILE, + 'send_yield' => true, + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + //小包 + for ($i = 0; $i < 1000; $i++) { + $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); + } + //慢速发送 + for ($i = 0; $i < 100; $i++) { + $serv->send($fd, str_repeat('A', rand(1000, 2000))); + usleep(rand(10000, 50000)); + $serv->send($fd, str_repeat('A', rand(2000, 4000)) . "\r\n\r\n"); + } + //大包 + for ($i = 0; $i < 1000; $i++) { + $data = base64_encode(random_bytes(random_int(20000, 256 * 1024))); + $md5 = md5($data); + $serv->send($fd, serialize(['i' => $i, 'md5' => $md5, 'data' => $data]) . "\r\n\r\n"); + } + }); + $serv->start(); +}; +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS +CLOSE diff --git a/tests/swoole_client_async/eof_close.phpt b/tests/swoole_client_async/eof_close.phpt new file mode 100644 index 00000000000..67b0b622d51 --- /dev/null +++ b/tests/swoole_client_async/eof_close.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_client_async: eof protocol [async] [close] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set(['open_eof_check' => true, 'open_eof_split' => true, "package_eof" => "\r\n\r\n"]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid, $pm) { + echo "RECEIVED\n"; + $cli->close(); + $pm->kill(); + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + echo "CLOSED\n"; + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 128 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); + }); + $serv->start(); +}; +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +RECEIVED +CLOSED diff --git a/tests/swoole_client_async/getSocket_bug.phpt b/tests/swoole_client_async/getSocket_bug.phpt new file mode 100644 index 00000000000..c3038e3c10c --- /dev/null +++ b/tests/swoole_client_async/getSocket_bug.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_async: getSocket debug +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) use ($timer) { + // getSocket BUG + $cli->getSocket(); + $cli->getSocket(); + + echo "SUCCESS\n"; + /* + @$cli->getSocket(); + $err = error_get_last(); + Assert::same($err["message"], "swoole_client_async::getSocket(): unable to obtain socket family Error: Bad file descriptor[9]."); + */ + $cli->close(); + Swoole\Timer::clear($timer); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error\n"; +}); +$cli->on("close", function (Swoole\Async\Client $cli) { +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/getpeername.phpt b/tests/swoole_client_async/getpeername.phpt new file mode 100644 index 00000000000..a37ca90698c --- /dev/null +++ b/tests/swoole_client_async/getpeername.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_client_async: getsockpeername +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) { + $cli = new \Swoole\Async\Client(SWOOLE_SOCK_UDP); + + $cli->on("connect", function (\Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $cli->send("test"); + }); + + $cli->on("receive", function (\Swoole\Async\Client $cli, $data) { + $i = $cli->getpeername(); + Assert::assert($i !== false); + $cli->send('shutdown'); + $cli->close(); + }); + + $cli->on("close", function (\Swoole\Async\Client $cli) { + echo "SUCCESS\n"; + }); + + $r = $cli->connect(UDP_SERVER_HOST, UDP_SERVER_PORT, 1); + Assert::assert($r); + Swoole\Event::wait(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new \Swoole\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_BASE, SWOOLE_SOCK_UDP); + $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("Packet", function (\Swoole\Server $serv, $data, $clientInfo) { + if (trim($data) == 'shutdown') { + $serv->shutdown(); + return; + } + $serv->sendto($clientInfo['address'], $clientInfo['port'], $data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/getsockname.phpt b/tests/swoole_client_async/getsockname.phpt new file mode 100644 index 00000000000..f4b84a61840 --- /dev/null +++ b/tests/swoole_client_async/getsockname.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client getsockname +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) use ($timer) { + Assert::true($cli->isConnected()); + + $i = $cli->getsockname(); + Assert::assert($i !== false); + Assert::same($i["host"], '127.0.0.1'); + + $cli->close(); + Swoole\Timer::clear($timer); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS"; + Swoole\Event::exit(); +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/length_protocol.phpt b/tests/swoole_client_async/length_protocol.phpt new file mode 100644 index 00000000000..0e59ba74d93 --- /dev/null +++ b/tests/swoole_client_async/length_protocol.phpt @@ -0,0 +1,111 @@ +--TEST-- +swoole_client_async: length protocol [async] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + static $i = 0; + $i++; + + //小包 + if ($i <= 1000) { + Assert::assert($pkg and strlen($pkg) <= 2048); + if ($i == 1000) { + echo "SUCCESS\n"; + } + return; + } //慢速发送 + elseif ($i <= 1100) { + Assert::assert($pkg and strlen($pkg) <= 8192); + if ($i == 1100) { + echo "SUCCESS\n"; + } + return; + } //大包 + else { + Assert::assert($pkg != false); + $_pkg = unserialize(substr($pkg, 4)); + Assert::assert(is_array($_pkg)); + Assert::same($_pkg['i'], $i - 1100 - 1); + Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024); + if ($i == 2100) { + echo "SUCCESS\n"; + $cli->close(); + Swoole\Process::kill($pid); + } + } + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 1, + 'send_yield' => true, + 'log_file' => '/tmp/swoole.log', + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + //小包 + for ($i = 0; $i < 1000; $i++) { + $data = str_repeat('A', rand(100, 2000)); + $serv->send($fd, pack('N', strlen($data)) . $data); + } + //慢速发送 + for ($i = 0; $i < 100; $i++) { + $data = str_repeat('A', rand(3000, 6000)); + $n = rand(1000, 2000); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); + usleep(rand(10000, 50000)); + $serv->send($fd, substr($data, $n)); + } + //大包 + for ($i = 0; $i < 1000; $i++) { + $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); + $serv->send($fd, pack('N', strlen($data)) . $data); + } + }); + $serv->start(); +}; + +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS diff --git a/tests/swoole_client_async/length_protocol_func.phpt b/tests/swoole_client_async/length_protocol_func.phpt new file mode 100644 index 00000000000..aa3a45718f7 --- /dev/null +++ b/tests/swoole_client_async/length_protocol_func.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_client_async: length protocol func +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_func' => function ($data) { + $n = strpos($data, '|'); + if ($n == false) { + return -1; + } else { + return intval(substr($data, 0, $n)) + $n + 1; + } + }, + ]); + $client->on("connect", function (Swoole\Async\Client $cli) { + $int = rand(1000, 5000); + $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]); + $cli->send(pack('N', strlen($data) + 4) . $data); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + Assert::assert($pkg != false and strlen($pkg) > 100); + Swoole\Process::kill($pid); + $cli->close(); + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 0, + ]); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $data = str_repeat('A', rand(100, 2000)); + $serv->send($fd, strlen($data) . "|" . $data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/port_invalid.phpt b/tests/swoole_client_async/port_invalid.phpt new file mode 100644 index 00000000000..387f27d863d --- /dev/null +++ b/tests/swoole_client_async/port_invalid.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_client_async: port invalid +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + +}); + +Assert::false(@$cli->connect("www.baidu.com", null, 2.0)); +Assert::same(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/sendfile.phpt b/tests/swoole_client_async/sendfile.phpt new file mode 100644 index 00000000000..722614ac770 --- /dev/null +++ b/tests/swoole_client_async/sendfile.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_client_async: async sendfile +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send(pack('N', filesize(TEST_IMAGE))); + $ret = $cli->sendfile(TEST_IMAGE); + Assert::assert($ret); + }); + $client->on("receive", function (Swoole\Async\Client $cli, $data) { + $cli->send(pack('N', 8) . 'shutdown'); + $cli->close(); + Assert::same($data, md5_file(TEST_IMAGE)); + }); + $client->on("error", function ($cli) { + echo "Connect failed\n"; + }); + $client->on("close", function ($cli) { + + }); + $client->connect(TCP_SERVER_HOST, $port, 0.5); + Swoole\Event::wait(); +}; + +$pm->childFunc = function () use ($pm, $port) { + $serv = new \Swoole\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv->set([ + "worker_num" => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'dispatch_mode' => 1, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'package_max_length' => 2000000, + ]); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("Receive", function (\Swoole\Server $serv, $fd, $rid, $data) { + if (substr($data, 4, 8) == 'shutdown') { + $serv->shutdown(); + return; + } + $serv->send($fd, md5(substr($data, 4))); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/sleep_wake.phpt b/tests/swoole_client_async/sleep_wake.phpt new file mode 100644 index 00000000000..1c3aed47b42 --- /dev/null +++ b/tests/swoole_client_async/sleep_wake.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client sleep & sleep +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $cli = new \Swoole\Async\Client(SWOOLE_SOCK_TCP); + + $cli->on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $r = $cli->sleep(); + Assert::assert($r); + swoole_timer_after(200, function () use ($cli) { + $r = $cli->wakeup(); + Assert::assert($r); + }); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + }); + + $cli->on("receive", function (Swoole\Async\Client $cli, $data) { + $recv_len = strlen($data); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + $cli->close(); + Assert::false($cli->isConnected()); + }); + + $cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; + }); + + $cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS"; + }); + + $cli->connect('127.0.0.1', $pm->getFreePort(), 0.1); + Swoole\Event::wait(); + Swoole\Process::kill($pid); +}; + +$pm->childFunc = function () use ($pm) { + include __DIR__ . "/../include/api/tcp_server.php"; +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_coro/bug_2346.phpt b/tests/swoole_client_coro/bug_2346.phpt index d94556e35c5..7819082d418 100644 --- a/tests/swoole_client_coro/bug_2346.phpt +++ b/tests/swoole_client_coro/bug_2346.phpt @@ -29,13 +29,13 @@ $pm->parentFunc = function () use ($pm) { $client->close(); }); Assert::assert(@!$client->recv(-1)); // connection closed - Assert::same($client->errCode, SOCKET_ECONNRESET); + Assert::same($client->errCode, SOCKET_ECANCELED); approximate(0.5, microtime(true) - $s); // canceled echo "DONE\n"; } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_client_coro/close.phpt b/tests/swoole_client_coro/close.phpt index 59678066980..2290d1e856e 100644 --- a/tests/swoole_client_coro/close.phpt +++ b/tests/swoole_client_coro/close.phpt @@ -19,6 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) 'package_body_offset' => 4, ]); $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->socket instanceof Swoole\Coroutine\Socket); $data = str_repeat('A', 1025); $cli->send(pack('N', strlen($data)).$data); co::sleep(0.2); @@ -26,14 +27,15 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::assert(is_string($retData) and strlen($retData) > 0); /** use valgrind to check memory */ $cli->close(); + Assert::eq($cli->socket, null); Assert::assert(!$cli->connected); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -44,11 +46,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('B', 1025); $serv->send($fd, pack('N', strlen($data)) . $data); diff --git a/tests/swoole_client_coro/close_in_other_co.phpt b/tests/swoole_client_coro/close_in_other_co.phpt index dea50d91327..ccbe7396f65 100644 --- a/tests/swoole_client_coro/close_in_other_co.phpt +++ b/tests/swoole_client_coro/close_in_other_co.phpt @@ -21,7 +21,8 @@ go(function () use ($client) { $client->connect('127.0.0.1', 9601); $data = @$client->recv(); //socket is closed - Assert::assert(!$data && $client->errCode === SOCKET_ECONNRESET); + Assert::assert(!$data ); + Assert::eq($client->errCode, SOCKET_ECANCELED); }); go(function () use ($client, $cid) { @@ -31,7 +32,7 @@ go(function () use ($client, $cid) { co::resume($cid); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_client_coro/close_resume.phpt b/tests/swoole_client_coro/close_resume.phpt index de24eb0302a..b90e6580252 100644 --- a/tests/swoole_client_coro/close_resume.phpt +++ b/tests/swoole_client_coro/close_resume.phpt @@ -27,12 +27,12 @@ $pm->parentFunc = function ($pid) use ($pm) $retData = $cli->recv(); Assert::same($retData, ''); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -43,11 +43,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('B', 1025); $serv->send($fd, pack('N', strlen($data)) . $data); diff --git a/tests/swoole_client_coro/close_socket_property.phpt b/tests/swoole_client_coro/close_socket_property.phpt new file mode 100644 index 00000000000..8f39c53e663 --- /dev/null +++ b/tests/swoole_client_coro/close_socket_property.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_client_coro: close socket property +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80)); + Assert::true($cli->connected); + Assert::true($cli->socket->close()); + Assert::false($cli->close()); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); + Assert::false($cli->connected); + Assert::null($cli->socket); + Assert::true($cli->connect('www.baidu.com', 80)); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/close_twice.phpt b/tests/swoole_client_coro/close_twice.phpt new file mode 100644 index 00000000000..15cdc44a7e8 --- /dev/null +++ b/tests/swoole_client_coro/close_twice.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_client_coro: close twice +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80); + Assert::true($cli->close()); + Assert::false($cli->close()); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/connect_with_dns.phpt b/tests/swoole_client_coro/connect_with_dns.phpt index 0dc742d2ea9..c428be567f1 100644 --- a/tests/swoole_client_coro/connect_with_dns.phpt +++ b/tests/swoole_client_coro/connect_with_dns.phpt @@ -10,7 +10,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); - Assert::true($cli->connect('www.tsinghua.edu.cn', 80)); + Assert::true($cli->connect('www.qq.com', 80)); }); ?> diff --git a/tests/swoole_client_coro/dtls.phpt b/tests/swoole_client_coro/dtls.phpt index 2197dce85a2..fdc36cb68f1 100644 --- a/tests/swoole_client_coro/dtls.phpt +++ b/tests/swoole_client_coro/dtls.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ //'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_client_coro/eof.phpt b/tests/swoole_client_coro/eof.phpt index fab76128e88..f862af5c242 100644 --- a/tests/swoole_client_coro/eof.phpt +++ b/tests/swoole_client_coro/eof.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( //'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); diff --git a/tests/swoole_client_coro/eof_02.phpt b/tests/swoole_client_coro/eof_02.phpt index 1e544108987..64e17fb0673 100644 --- a/tests/swoole_client_coro/eof_02.phpt +++ b/tests/swoole_client_coro/eof_02.phpt @@ -64,12 +64,12 @@ $pm->parentFunc = function ($pid) use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('0.0.0.0', 8000, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server('0.0.0.0', 8000, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set(array( 'open_eof_split' => true, 'package_eof' => "\r\n", diff --git a/tests/swoole_client_coro/eof_03.phpt b/tests/swoole_client_coro/eof_03.phpt index 32e53254a4c..9e5d3ae999d 100644 --- a/tests/swoole_client_coro/eof_03.phpt +++ b/tests/swoole_client_coro/eof_03.phpt @@ -3,7 +3,7 @@ swoole_client_coro: eof with smtp qq --SKIPIF-- --FILE-- diff --git a/tests/swoole_client_coro/export_socket.phpt b/tests/swoole_client_coro/export_socket.phpt new file mode 100644 index 00000000000..a5028722050 --- /dev/null +++ b/tests/swoole_client_coro/export_socket.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_client_coro: close socket property +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80)); + Assert::true($cli->connected); + $socket = $cli->exportSocket(); + $socket->close(); + Assert::false($cli->recv()); + Assert::false($cli->close()); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/fixed_package.phpt b/tests/swoole_client_coro/fixed_package.phpt index 83fea9f67a7..f8710bf78d3 100644 --- a/tests/swoole_client_coro/fixed_package.phpt +++ b/tests/swoole_client_coro/fixed_package.phpt @@ -31,7 +31,7 @@ $pm->parentFunc = function () use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_client_coro/length_01.phpt b/tests/swoole_client_coro/length_01.phpt index 60617a6fab4..93356d872c6 100644 --- a/tests/swoole_client_coro/length_01.phpt +++ b/tests/swoole_client_coro/length_01.phpt @@ -26,12 +26,12 @@ $pm->parentFunc = function ($pid) use ($pm) $len = unpack('Nlen', $retData)['len']; Assert::same(strlen($retData), $len + 4); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -42,11 +42,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $len = rand(512*1024, 1024*1024 - 4); $data = pack('N',$len ).str_repeat('C', $len); diff --git a/tests/swoole_client_coro/length_02.phpt b/tests/swoole_client_coro/length_02.phpt index a9942fd6980..6a1faa464b2 100644 --- a/tests/swoole_client_coro/length_02.phpt +++ b/tests/swoole_client_coro/length_02.phpt @@ -25,12 +25,12 @@ $pm->parentFunc = function ($pid) use ($pm, $port) $retData = $cli->recv(); Assert::same($retData, ''); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -41,11 +41,11 @@ $pm->childFunc = function () use ($pm, $port) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->send($fd, pack('N', 1223)); $serv->close($fd); diff --git a/tests/swoole_client_coro/length_03.phpt b/tests/swoole_client_coro/length_03.phpt index 23c1f847f06..135636772f9 100644 --- a/tests/swoole_client_coro/length_03.phpt +++ b/tests/swoole_client_coro/length_03.phpt @@ -5,13 +5,13 @@ swoole_client_coro: length protocol 03 --FILE-- parentFunc = function ($pid) use ($pm) -{ - go(function() use ($pm){ +$pm->parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $client->set([ 'open_length_check' => true, @@ -20,77 +20,67 @@ $pm->parentFunc = function ($pid) use ($pm) 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); $_pkg = unserialize(substr($pkg, 4)); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( - 'package_max_length' => 1024 * 1024 * 2, //2M - 'socket_buffer_size' => 256 * 1024 * 1024, - "worker_num" => 1, - 'log_file' => '/tmp/swoole.log', + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 256 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data)) . $data); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $data = str_repeat('A', rand(3000, 6000)); $n = rand(1000, 2000); - $serv->send($fd, pack('N', strlen($data)). substr($data, 0, $n)); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); usleep(rand(10000, 50000)); $serv->send($fd, substr($data, $n)); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); $serv->send($fd, pack('N', strlen($data)) . $data); } diff --git a/tests/swoole_client_coro/length_04.phpt b/tests/swoole_client_coro/length_04.phpt index 93bf74310e6..61fe077959e 100644 --- a/tests/swoole_client_coro/length_04.phpt +++ b/tests/swoole_client_coro/length_04.phpt @@ -32,12 +32,12 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($header['len'], 2); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -48,11 +48,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $header = unpack('nlen', $data); Assert::same(strlen($data), 2); diff --git a/tests/swoole_client_coro/length_protocol_func.phpt b/tests/swoole_client_coro/length_protocol_func.phpt index 8b7d7519433..a088ce8c37a 100644 --- a/tests/swoole_client_coro/length_protocol_func.phpt +++ b/tests/swoole_client_coro/length_protocol_func.phpt @@ -39,7 +39,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -49,10 +49,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, strlen($data) . "|" . $data); diff --git a/tests/swoole_client_coro/length_types.phpt b/tests/swoole_client_coro/length_types.phpt index 252a32ad98b..5f1b0bf0ee7 100644 --- a/tests/swoole_client_coro/length_types.phpt +++ b/tests/swoole_client_coro/length_types.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function () use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_client_coro/read_and_write.phpt b/tests/swoole_client_coro/read_and_write.phpt index 8d95c87fae6..5927430dd34 100644 --- a/tests/swoole_client_coro/read_and_write.phpt +++ b/tests/swoole_client_coro/read_and_write.phpt @@ -72,13 +72,13 @@ $pm->parentFunc = function ($pid) use ($pm) } }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'open_eof_split' => true, diff --git a/tests/swoole_client_coro/reconnect.phpt b/tests/swoole_client_coro/reconnect.phpt index 2d897bbb66c..9dadbaf2bf5 100644 --- a/tests/swoole_client_coro/reconnect.phpt +++ b/tests/swoole_client_coro/reconnect.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_client_coro: reconnect +swoole_client_coro: reconnect 1 --SKIPIF-- --FILE-- @@ -42,10 +42,11 @@ $pm->childFunc = function () use ($pm) { $serv->set([ 'log_file' => '/dev/null', ]); - + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); $serv->on('Receive', function () { }); - $serv->start(); }; $pm->childFirst(); diff --git a/tests/swoole_client_coro/reconnect_2.phpt b/tests/swoole_client_coro/reconnect_2.phpt new file mode 100644 index 00000000000..29dd30fb0f3 --- /dev/null +++ b/tests/swoole_client_coro/reconnect_2.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_coro: reconnect 2 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $flag = 0; + $client = new Client(SWOOLE_SOCK_TCP); + + $n = 2; + while ($n--) { + Assert::true($client->connect('127.0.0.1', 9501)); + Assert::true($client->close()); + } + echo "DONE\n"; + }); + + $pm->kill(); + +}; +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', 9501); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('Receive', function () { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_client_coro/reconnect_3.phpt b/tests/swoole_client_coro/reconnect_3.phpt new file mode 100644 index 00000000000..800ea9385c7 --- /dev/null +++ b/tests/swoole_client_coro/reconnect_3.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_client_coro: reconnect 3 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $flag = 0; + $client = new Client(SWOOLE_SOCK_TCP); + + $n = 2; + while ($n--) { + Assert::true($client->connect('127.0.0.1', 9501)); + go(function () use ($client) { + while (1) { + if (!$client->recv()) { + break; + } + } + Assert::true($client->close()); + }); + Assert::false($client->close()); + Assert::eq($client->errCode, SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT); + } + echo "DONE\n"; + }); + + $pm->kill(); + +}; +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', 9501); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('Receive', function () { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_client_coro/recv_after_close.phpt b/tests/swoole_client_coro/recv_after_close.phpt index 0eea9ef7755..6a13a127fa4 100644 --- a/tests/swoole_client_coro/recv_after_close.phpt +++ b/tests/swoole_client_coro/recv_after_close.phpt @@ -21,12 +21,12 @@ $pm->parentFunc = function ($pid) use ($pm) $cli->close(); Assert::assert(!$cli->connected); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -37,11 +37,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->close($fd); }); diff --git a/tests/swoole_client_coro/recv_timeout.phpt b/tests/swoole_client_coro/recv_timeout.phpt index cd367739f35..24c6d39c658 100644 --- a/tests/swoole_client_coro/recv_timeout.phpt +++ b/tests/swoole_client_coro/recv_timeout.phpt @@ -18,21 +18,21 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::false($retData); Assert::same($cli->errCode, SOCKET_ETIMEDOUT); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //no response }); diff --git a/tests/swoole_client_coro/recv_timeout2.phpt b/tests/swoole_client_coro/recv_timeout2.phpt index f7dd1d348d3..f3bc423b4e6 100644 --- a/tests/swoole_client_coro/recv_timeout2.phpt +++ b/tests/swoole_client_coro/recv_timeout2.phpt @@ -19,21 +19,21 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($cli->errCode, SOCKET_ETIMEDOUT); approximate(0.5, microtime(true) - $s); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //no response }); diff --git a/tests/swoole_client_coro/recvfrom.phpt b/tests/swoole_client_coro/recvfrom.phpt index 16106b04396..943888ab8f6 100644 --- a/tests/swoole_client_coro/recvfrom.phpt +++ b/tests/swoole_client_coro/recvfrom.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_client_coro: sendto +swoole_client_coro: recvfrom 1 --SKIPIF-- --FILE-- @@ -25,7 +25,7 @@ go(function () use ($free_port) { Assert::same($port, $free_port); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- hello diff --git a/tests/swoole_client_coro/recvfrom_2.phpt b/tests/swoole_client_coro/recvfrom_2.phpt new file mode 100644 index 00000000000..3c792fdff14 --- /dev/null +++ b/tests/swoole_client_coro/recvfrom_2.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_client_coro: recvfrom 2 +--SKIPIF-- + +--FILE-- +set([ + 'bind_address' => '127.0.0.1', + 'bind_port' => $free_port, + ]); + $n = N; + while ($n--) { + $data = $cli->recvfrom(1024, $addr, $port); + Assert::same($data, 'hello'); + } + echo "DONE\n"; +}); + +go(function () use ($free_port) { + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $n = N; + while ($n--) { + $socket->sendto('127.0.0.1', $free_port, "hello"); + Co::sleep(0.01); + } +}); +Swoole\Event::wait(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_client_coro/send_big.phpt b/tests/swoole_client_coro/send_big.phpt index 796f1c79e43..93afffbdab5 100644 --- a/tests/swoole_client_coro/send_big.phpt +++ b/tests/swoole_client_coro/send_big.phpt @@ -32,13 +32,13 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($data, "HTTP/1.1 200 OK\r\n\r\n"); $client->close(); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) + $serv->on('connect', function (Swoole\Server $serv, $fd) { }); diff --git a/tests/swoole_client_coro/tcp_client.phpt b/tests/swoole_client_coro/tcp_client.phpt index cb5364aad98..bd9d0003bc2 100644 --- a/tests/swoole_client_coro/tcp_client.phpt +++ b/tests/swoole_client_coro/tcp_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); if (!$cli->connect('127.0.0.1', $pm->getFreePort(1))) diff --git a/tests/swoole_client_coro/tcp_nodelay.phpt b/tests/swoole_client_coro/tcp_nodelay.phpt index d071561ebf5..62e89745de8 100644 --- a/tests/swoole_client_coro/tcp_nodelay.phpt +++ b/tests/swoole_client_coro/tcp_nodelay.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $cli->set(['open_tcp_nodelay' => false]); diff --git a/tests/swoole_client_coro/timeout.phpt b/tests/swoole_client_coro/timeout.phpt index abed185cb91..3aaba095b99 100644 --- a/tests/swoole_client_coro/timeout.phpt +++ b/tests/swoole_client_coro/timeout.phpt @@ -53,7 +53,7 @@ go(function () use ($port) { echo "TIMEOUT\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- recvfrom client diff --git a/tests/swoole_client_coro/udp_client.phpt b/tests/swoole_client_coro/udp_client.phpt index b8db7da0e54..520c0ff4faf 100644 --- a/tests/swoole_client_coro/udp_client.phpt +++ b/tests/swoole_client_coro/udp_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_UDP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); if (!$cli->connect('127.0.0.1', $pm->getFreePort(1))) diff --git a/tests/swoole_client_sync/bind_address.phpt b/tests/swoole_client_sync/bind_address.phpt index 68e47e87865..32e9c739e04 100644 --- a/tests/swoole_client_sync/bind_address.phpt +++ b/tests/swoole_client_sync/bind_address.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/connect_1.phpt b/tests/swoole_client_sync/connect_1.phpt index a03c4751f5d..a2da61523e9 100644 --- a/tests/swoole_client_sync/connect_1.phpt +++ b/tests/swoole_client_sync/connect_1.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; ini_set('swoole.display_errors', 'off'); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect("11.11.11.11", 80, 0.5); Assert::false($r); Assert::eq($cli->errCode, SOCKET_ETIMEDOUT); diff --git a/tests/swoole_client_sync/connect_2.phpt b/tests/swoole_client_sync/connect_2.phpt index 067a0f4a5f5..d0ac6a5935b 100644 --- a/tests/swoole_client_sync/connect_2.phpt +++ b/tests/swoole_client_sync/connect_2.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; killself_in_syncmode(1000, SIGTERM); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); Assert::assert($r); $cli->close(); diff --git a/tests/swoole_client_sync/connect_3.phpt b/tests/swoole_client_sync/connect_3.phpt index 8cb5294a6de..c92c17393c7 100644 --- a/tests/swoole_client_sync/connect_3.phpt +++ b/tests/swoole_client_sync/connect_3.phpt @@ -7,7 +7,7 @@ swoole_client_sync: connect 1 - 3 nonblocking connect & select require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 1); Assert::assert($r); $r = $w = $e = [$cli]; @@ -21,12 +21,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/enableSSL.phpt b/tests/swoole_client_sync/enableSSL.phpt new file mode 100644 index 00000000000..37e17f59d43 --- /dev/null +++ b/tests/swoole_client_sync/enableSSL.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +connect("www.baidu.com", 443, 2.0)); + +if ($cli->enableSSL()) { + echo "SSL READY\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"); +} + +$resp = ''; +while (true) { + $data = $cli->recv(); + if ($data == false) { + break; + } + $resp .= $data; +} + +Assert::assert(strlen($resp) > 0); +Assert::contains($resp, 'www.baidu.com'); +$cli->close(); +echo "DONE\n"; +?> +--EXPECT-- +SSL READY +DONE diff --git a/tests/swoole_client_sync/enableSSL_2.phpt b/tests/swoole_client_sync/enableSSL_2.phpt new file mode 100644 index 00000000000..aa787584a8a --- /dev/null +++ b/tests/swoole_client_sync/enableSSL_2.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +connect("www.baidu.com", 443, 2.0)); + +try { + $cli->enableSSL(function (){}); +} catch (\Throwable $e) { + Assert::contains($e->getMessage(), 'not support `onSslReady` callback'); +} +?> +--EXPECT-- diff --git a/tests/swoole_client_sync/eof.phpt b/tests/swoole_client_sync/eof.phpt index 46819dde468..7da34170476 100644 --- a/tests/swoole_client_sync/eof.phpt +++ b/tests/swoole_client_sync/eof.phpt @@ -8,12 +8,10 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $port = get_one_free_port(); -$pm->parentFunc = function ($pid) use ($port) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$pm->parentFunc = function ($pid) use ($port) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); - if (!$client->connect('127.0.0.1', $port, 5, 0)) - { + if (!$client->connect('127.0.0.1', $port, 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -21,38 +19,34 @@ $pm->parentFunc = function ($pid) use ($port) $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); - $_pkg = unserialize($pkg); + $_pkg = swoole_substr_unserialize($pkg, 0, strlen($pkg) - 4); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -62,27 +56,22 @@ $pm->childFunc = function () use ($pm, $port) "worker_num" => 1, 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $serv->send($fd, str_repeat('A', rand(1000, 2000))); usleep(rand(10000, 50000)); $serv->send($fd, str_repeat('A', rand(2000, 4000)) . "\r\n\r\n"); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $serv->send($fd, serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]) . "\r\n\r\n"); } }); diff --git a/tests/swoole_client_sync/eof_close.phpt b/tests/swoole_client_sync/eof_close.phpt new file mode 100644 index 00000000000..484dc19964c --- /dev/null +++ b/tests/swoole_client_sync/eof_close.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_client_sync: eof protocol [sync] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port, $pkg, $pm) { + $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set(['open_eof_check' => true, "package_eof" => EOF]); + if (!$client->connect('127.0.0.1', $port, 5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $client->send("recv\r\n\r\n"); + $recvPkg = $client->recv(); + Assert::assert($recvPkg != false); + $_pkg = swoole_substr_unserialize($recvPkg, 0, strlen($recvPkg) - 4); + Assert::assert(is_array($_pkg)); + Assert::eq($_pkg['data'], $pkg); + $recvPkg = $client->recv(); + Assert::same($recvPkg, ''); + echo "SUCCESS\n"; + $client->close(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $port, $pkg) { + $serv = new Server('127.0.0.1', $port, SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, + 'socket_buffer_size' => 256 * 1024 * 1024, + 'log_file' => TEST_LOG_FILE, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pkg) { + $serv->send($fd, serialize(['data' => $pkg]) . EOF); + $serv->close($fd); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_sync/eof_timeout.phpt b/tests/swoole_client_sync/eof_timeout.phpt index 834d3961d00..a288866f5e3 100644 --- a/tests/swoole_client_sync/eof_timeout.phpt +++ b/tests/swoole_client_sync/eof_timeout.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { if (!$cli->connect('127.0.0.1', $pm->getFreePort(1), 0.5)) { fail: echo "ERROR\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); return; } //no eof, should be timeout here @@ -29,11 +29,11 @@ $pm->parentFunc = function ($pid) use ($pm) { goto fail; } echo "OK\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -45,14 +45,14 @@ $pm->childFunc = function () use ($pm) { $http->set([ //'log_file' => '/dev/null' ]); - $http->on("WorkerStart", function (\swoole_server $serv) { + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); $http->start(); diff --git a/tests/swoole_client_sync/http_proxy.phpt b/tests/swoole_client_sync/http_proxy.phpt new file mode 100644 index 00000000000..0eb8fab5cf4 --- /dev/null +++ b/tests/swoole_client_sync/http_proxy.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_sync: http client with http_proxy +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => 30, + 'http_proxy_host' => HTTP_PROXY_HOST, + 'http_proxy_port' => HTTP_PROXY_PORT +]); +client_http_v10_get($cli) +?> +--EXPECT-- diff --git a/tests/swoole_client_sync/keep1.phpt b/tests/swoole_client_sync/keep1.phpt index be82e7c8a12..16109fbd7c4 100644 --- a/tests/swoole_client_sync/keep1.phpt +++ b/tests/swoole_client_sync/keep1.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep3.phpt b/tests/swoole_client_sync/keep3.phpt index 62cc0f5671b..ec4e67ac105 100644 --- a/tests/swoole_client_sync/keep3.phpt +++ b/tests/swoole_client_sync/keep3.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep4.phpt b/tests/swoole_client_sync/keep4.phpt index 7cccf9e7d08..319274c53eb 100644 --- a/tests/swoole_client_sync/keep4.phpt +++ b/tests/swoole_client_sync/keep4.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep5.phpt b/tests/swoole_client_sync/keep5.phpt index 1037aa279c4..5c8f198370a 100644 --- a/tests/swoole_client_sync/keep5.phpt +++ b/tests/swoole_client_sync/keep5.phpt @@ -31,12 +31,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep6.phpt b/tests/swoole_client_sync/keep6.phpt index b2dd0be5aad..632b4033569 100644 --- a/tests/swoole_client_sync/keep6.phpt +++ b/tests/swoole_client_sync/keep6.phpt @@ -33,12 +33,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); usleep(5000); $serv->close($fd); diff --git a/tests/swoole_client_sync/length_protocol.phpt b/tests/swoole_client_sync/length_protocol.phpt index 8888f820863..74f63f1ab69 100644 --- a/tests/swoole_client_sync/length_protocol.phpt +++ b/tests/swoole_client_sync/length_protocol.phpt @@ -7,9 +7,8 @@ swoole_client_sync: length protocol [sync] require __DIR__ . '/../include/bootstrap.php'; $port = get_one_free_port(); $pm = new ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$pm->parentFunc = function ($pid) use ($port) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -17,8 +16,7 @@ $pm->parentFunc = function ($pid) use ($port) 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - if (!$client->connect('127.0.0.1', $port, 0.5, 0)) - { + if (!$client->connect('127.0.0.1', $port, 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -26,68 +24,59 @@ $pm->parentFunc = function ($pid) use ($port) $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); $_pkg = unserialize(substr($pkg, 4)); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( - 'package_max_length' => 1024 * 1024 * 2, //2M - 'socket_buffer_size' => 256 * 1024 * 1024, - "worker_num" => 1, - 'log_file' => '/tmp/swoole.log', + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 256 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data)) . $data); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $data = str_repeat('A', rand(3000, 6000)); $n = rand(1000, 2000); - $serv->send($fd, pack('N', strlen($data)). substr($data, 0, $n)); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); usleep(rand(10000, 50000)); $serv->send($fd, substr($data, $n)); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); $serv->send($fd, pack('N', strlen($data)) . $data); } diff --git a/tests/swoole_client_sync/length_protocol_02.phpt b/tests/swoole_client_sync/length_protocol_02.phpt index d0c34bd9fb2..57b13c4c916 100644 --- a/tests/swoole_client_sync/length_protocol_02.phpt +++ b/tests/swoole_client_sync/length_protocol_02.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -26,11 +26,11 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->send(pack('N', strlen($data) + 4) . $data); $pkg = $client->recv(); Assert::assert($pkg != false and strlen($pkg) > 100); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -40,10 +40,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data) + 4) . $data); }); diff --git a/tests/swoole_client_sync/length_protocol_func.phpt b/tests/swoole_client_sync/length_protocol_func.phpt index 755bf8f1f42..358d2f70dab 100644 --- a/tests/swoole_client_sync/length_protocol_func.phpt +++ b/tests/swoole_client_sync/length_protocol_func.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -34,11 +34,11 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->send(pack('N', strlen($data) + 4) . $data); $pkg = $client->recv(); Assert::assert($pkg != false and strlen($pkg) > 100); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -48,10 +48,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, strlen($data) . "|" . $data); diff --git a/tests/swoole_client_sync/recv_in_task.phpt b/tests/swoole_client_sync/recv_in_task.phpt index 435b00c792a..82992e31461 100644 --- a/tests/swoole_client_sync/recv_in_task.phpt +++ b/tests/swoole_client_sync/recv_in_task.phpt @@ -10,23 +10,23 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', 0, SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', 0, SWOOLE_BASE); $http->set(['worker_num' => 1, 'task_worker_num'=>1, 'log_file' => '/dev/null']); - $http->on('workerStart', function (swoole_server $server, int $worker_id) use ($pm) { + $http->on('workerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { if (!$server->taskworker) { // start logic in task $server->task(1, 0); } }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { usleep(100 * 1000); $response->end('OK'); }); - $http->on('Task', function(swoole_server $serv, int $task_id, int $src_worker_id, $data) use ($pm) { + $http->on('Task', function(Swoole\Server $serv, int $task_id, int $src_worker_id, $data) use ($pm) { // trigger timer constantly in order to trigger the EINTR internally - swoole_timer_tick(1, function(){}); + Swoole\Timer::tick(1, function(){}); // send request - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $p = $serv->ports[0]->port; Assert::assert($cli->connect('127.0.0.1', $p, 3)); $request = "GET / HTTP/1.1\r\n\r\n"; @@ -42,7 +42,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $http->on('Finish', function(){}); - $http->addProcess(new swoole_process(function (swoole_process $p) { + $http->addProcess(new Swoole\Process(function (Swoole\Process $p) { })); $http->start(); }; diff --git a/tests/swoole_client_sync/recv_timeout.phpt b/tests/swoole_client_sync/recv_timeout.phpt index 3ae9bedcdd8..807fc6d8086 100644 --- a/tests/swoole_client_sync/recv_timeout.phpt +++ b/tests/swoole_client_sync/recv_timeout.phpt @@ -20,15 +20,15 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new \swoole_server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { //do nothing }); $serv->start(); diff --git a/tests/swoole_client_sync/recv_with_open_eof_check.phpt b/tests/swoole_client_sync/recv_with_open_eof_check.phpt index a60bbf9b9bd..db424e34dc5 100644 --- a/tests/swoole_client_sync/recv_with_open_eof_check.phpt +++ b/tests/swoole_client_sync/recv_with_open_eof_check.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $serv = new Server("127.0.0.1", $pm->getFreePort()); + $serv = new Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, diff --git a/tests/swoole_client_sync/select.phpt b/tests/swoole_client_sync/select.phpt index 1918426cf95..4d52dfd37a9 100644 --- a/tests/swoole_client_sync/select.phpt +++ b/tests/swoole_client_sync/select.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) $clients[$client->sock] = $client; } } - + $s = microtime(true); while (!empty($clients)) { $write = $error = array(); @@ -59,10 +59,10 @@ $pm->childFunc = function () use ($pm) "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_client_sync/send_recv.phpt b/tests/swoole_client_sync/send_recv.phpt index 2320f8eeda2..ccd8eddcead 100644 --- a/tests/swoole_client_sync/send_recv.phpt +++ b/tests/swoole_client_sync/send_recv.phpt @@ -7,7 +7,7 @@ swoole_client_sync: send & recv require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort())); $request = "GET / HTTP/1.1\r\n\r\n"; Assert::same($cli->send($request), strlen($request)); @@ -18,12 +18,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/sendfile.phpt b/tests/swoole_client_sync/sendfile.phpt index b061760402a..94ea41897af 100644 --- a/tests/swoole_client_sync/sendfile.phpt +++ b/tests/swoole_client_sync/sendfile.phpt @@ -1,14 +1,13 @@ --TEST-- swoole_client_sync: sync sendfile --SKIPIF-- - + --FILE-- parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $client->connect(TCP_SERVER_HOST, $port, 0.5); Assert::assert($r); @@ -22,9 +21,8 @@ $pm->parentFunc = function ($pid) use ($port) Assert::same($data, md5_file(TEST_IMAGE)); }; -$pm->childFunc = function () use ($pm, $port) -{ - $serv = new \swoole_server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -35,14 +33,11 @@ $pm->childFunc = function () use ($pm, $port) 'package_body_offset' => 4, 'package_max_length' => 2000000, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) - { - if (substr($data, 4, 8) == 'shutdown') - { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { + if (substr($data, 4, 8) == 'shutdown') { $serv->shutdown(); return; } diff --git a/tests/swoole_client_sync/socks5_proxy.phpt b/tests/swoole_client_sync/socks5_proxy.phpt new file mode 100644 index 00000000000..5370664da42 --- /dev/null +++ b/tests/swoole_client_sync/socks5_proxy.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_sync: http client with http_proxy +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => 30, + 'socks5_host' => SOCKS5_PROXY_HOST, + 'socks5_port' => SOCKS5_PROXY_PORT +]); +client_http_v10_get($cli) +?> +--EXPECT-- diff --git a/tests/swoole_client_sync/ssl_recv_timeout.phpt b/tests/swoole_client_sync/ssl_recv_timeout.phpt index 30f57c044bd..96541cb34dd 100644 --- a/tests/swoole_client_sync/ssl_recv_timeout.phpt +++ b/tests/swoole_client_sync/ssl_recv_timeout.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 5); Assert::assert($r); $cli->send("hello world\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data"); }); $serv->start(); diff --git a/tests/swoole_client_sync/sync_send_recv.phpt b/tests/swoole_client_sync/sync_send_recv.phpt index a24f8631e68..b8680dac7f0 100644 --- a/tests/swoole_client_sync/sync_send_recv.phpt +++ b/tests/swoole_client_sync/sync_send_recv.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; $simple_tcp_server = __DIR__ . "/../include/api/swoole_server/simple_udp_server.php"; start_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT); -$client = new swoole_client(SWOOLE_SOCK_UDP); +$client = new Swoole\Client(SWOOLE_SOCK_UDP); $client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT); $data = "UdpSendto"; diff --git a/tests/swoole_client_sync/udg_send_timeout.phpt b/tests/swoole_client_sync/udg_send_timeout.phpt index ac28a3ce9dd..f10b9239180 100644 --- a/tests/swoole_client_sync/udg_send_timeout.phpt +++ b/tests/swoole_client_sync/udg_send_timeout.phpt @@ -9,7 +9,7 @@ const N = 65507; define("SOCKET_FILE", __DIR__.'/server.sock'); $socket = stream_socket_server("udg://".SOCKET_FILE, $errno, $errstr, STREAM_SERVER_BIND); -$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM); +$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM); $client->connect(SOCKET_FILE, 0, 0.3); $s = microtime(true); diff --git a/tests/swoole_client_sync/udp_client_sendto.phpt b/tests/swoole_client_sync/udp_client_sendto.phpt index 8a7cd61637e..e7ca6a97d35 100644 --- a/tests/swoole_client_sync/udp_client_sendto.phpt +++ b/tests/swoole_client_sync/udp_client_sendto.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; $simple_tcp_server = __DIR__ . "/../include/api/swoole_server/simple_udp_server.php"; start_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT); -$client = new swoole_client(SWOOLE_SOCK_UDP); +$client = new Swoole\Client(SWOOLE_SOCK_UDP); $client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT); $ret = $client->sendto(UDP_SERVER_HOST, UDP_SERVER_PORT, "TestUdpClientSendto"); diff --git a/tests/swoole_coroutine/after_start_server_1.phpt b/tests/swoole_coroutine/after_start_server_1.phpt index e110ad1d12b..af377265b2f 100644 --- a/tests/swoole_coroutine/after_start_server_1.phpt +++ b/tests/swoole_coroutine/after_start_server_1.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { Co::readFile(__FILE__); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_coroutine/all_asleep.phpt b/tests/swoole_coroutine/all_asleep.phpt index 3b3dc2cc1d3..b0be8c31922 100644 --- a/tests/swoole_coroutine/all_asleep.phpt +++ b/tests/swoole_coroutine/all_asleep.phpt @@ -41,14 +41,14 @@ echo "DONE\n"; [Coroutine-3] -------------------------------------------------------------------- -#0 Swoole\Coroutine::yield() called at [%s:%d] -#1 test2() called at [%s:%d] +%A +%A [Coroutine-2] -------------------------------------------------------------------- -#0 Swoole\Coroutine::yield() called at [%s:%d] -#1 {closure}() called at [%s:%d] -#2 test1() called at [%s:%d] +%A +%A +%A DONE diff --git a/tests/swoole_coroutine/autoload.phpt b/tests/swoole_coroutine/autoload.phpt new file mode 100644 index 00000000000..f3bd94d5db4 --- /dev/null +++ b/tests/swoole_coroutine/autoload.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_coroutine: autoload +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +object(SwooleTestClassA)#%d (0) { +} +object(SwooleTestClassA)#%d (0) { +} diff --git a/tests/swoole_coroutine/autoload_not_found.phpt b/tests/swoole_coroutine/autoload_not_found.phpt new file mode 100644 index 00000000000..eb6aeb40f78 --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_found.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine: autoload not found +--SKIPIF-- + +--FILE-- +getMessage(), 'Class "SwooleTestClassA2" not found'); + echo "DONE\n"; + } + +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt b/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt new file mode 100644 index 00000000000..4784562eac7 --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_coroutine: autoload not found and not in coroutine +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d +Stack trace: +#0 %s(%d): %s +#1 %s(%d): %s +#2 {main} + thrown in %s on line %d diff --git a/tests/swoole_coroutine/autoload_not_in_coroutine.phpt b/tests/swoole_coroutine/autoload_not_in_coroutine.phpt new file mode 100644 index 00000000000..2eff413ae66 --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_in_coroutine.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_coroutine: autoload not in coroutine +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +object(SwooleTestClassA)#%d (0) { +} +object(SwooleTestClassA)#%d (0) { +} diff --git a/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt new file mode 100644 index 00000000000..1ad33d2c79d --- /dev/null +++ b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/bailout: call co redis in shutdown function +--SKIPIF-- + +--FILE-- +connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); + register_shutdown_function(function () use ($redis) { + $redis->get('key'); + }); + usleep(10000); +}); + +Event::wait(); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: API must be called in the coroutine in %s:%d +Stack trace: +#0 %s(%d): Redis->get('key') +#1 [internal function]: {closure%S}() +#2 {main} + thrown in %s on line %d diff --git a/tests/swoole_coroutine/bailout/error.phpt b/tests/swoole_coroutine/bailout/error.phpt index d775de3d2fc..b3f9f591866 100644 --- a/tests/swoole_coroutine/bailout/error.phpt +++ b/tests/swoole_coroutine/bailout/error.phpt @@ -19,6 +19,6 @@ Event::wait(); --EXPECTF-- Fatal error: Uncaught Error in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d shutdown diff --git a/tests/swoole_coroutine/bailout/error_in.phpt b/tests/swoole_coroutine/bailout/error_in.phpt index 219013e38ae..ebbe0387cb3 100644 --- a/tests/swoole_coroutine/bailout/error_in.phpt +++ b/tests/swoole_coroutine/bailout/error_in.phpt @@ -1,10 +1,13 @@ --TEST-- swoole_coroutine/bailout: error in the coroutine --SKIPIF-- - + --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function a() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/bailout/error_out.phpt b/tests/swoole_coroutine/bailout/error_out.phpt index a25770a92de..ab22dd722ba 100644 --- a/tests/swoole_coroutine/bailout/error_out.phpt +++ b/tests/swoole_coroutine/bailout/error_out.phpt @@ -1,10 +1,13 @@ --TEST-- swoole_coroutine/bailout: error out of the coroutine --SKIPIF-- - + --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function a() in %s:%d diff --git a/tests/swoole_coroutine/bailout/exit.phpt b/tests/swoole_coroutine/bailout/exit.phpt index 95d1e4e825b..ea233b75311 100644 --- a/tests/swoole_coroutine/bailout/exit.phpt +++ b/tests/swoole_coroutine/bailout/exit.phpt @@ -10,12 +10,10 @@ $process = new Swoole\Process(function () { echo 'shutdown' . PHP_EOL; }); go(function () { - try - { + try { exit(0); - } catch (Swoole\ExitException $e) - { - echo $e->getMessage()."\n"; + } catch (Swoole\ExitException $e) { + echo $e->getMessage() . "\n"; } }); }); diff --git a/tests/swoole_coroutine/before_create_server_1.phpt b/tests/swoole_coroutine/before_create_server_1.phpt index ac66e852077..90c4a96ad36 100644 --- a/tests/swoole_coroutine/before_create_server_1.phpt +++ b/tests/swoole_coroutine/before_create_server_1.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { go(function () use ($pm) { Co::sleep(0.1); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', "worker_num" => 1, diff --git a/tests/swoole_coroutine/before_create_server_2.phpt b/tests/swoole_coroutine/before_create_server_2.phpt index 6c850596e3e..792ac463ffe 100644 --- a/tests/swoole_coroutine/before_create_server_2.phpt +++ b/tests/swoole_coroutine/before_create_server_2.phpt @@ -23,9 +23,9 @@ $pm->childFunc = function () use ($pm) { co::sleep(0.1); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); diff --git a/tests/swoole_coroutine/before_create_server_3.phpt b/tests/swoole_coroutine/before_create_server_3.phpt index 24ed37bc3eb..2f8399b2fba 100644 --- a/tests/swoole_coroutine/before_create_server_3.phpt +++ b/tests/swoole_coroutine/before_create_server_3.phpt @@ -23,9 +23,9 @@ $pm->childFunc = function () use ($pm) { Co::readFile(__FILE__); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); diff --git a/tests/swoole_coroutine/bug_2387.phpt b/tests/swoole_coroutine/bug_2387.phpt index c705d2975a4..e0dffd0f898 100644 --- a/tests/swoole_coroutine/bug_2387.phpt +++ b/tests/swoole_coroutine/bug_2387.phpt @@ -33,7 +33,8 @@ $pm->childFunc = function () use ($pm) { $httpServer = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $httpServer->set([ 'log_file' => '/dev/null', - 'worker_num' => 1 + 'worker_num' => 1, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $httpServer->on('WorkerStart', function (Swoole\Http\Server $server) use ($pm, $config) { try { diff --git a/tests/swoole_coroutine/call_not_exists_func.phpt b/tests/swoole_coroutine/call_not_exists_func.phpt index 234cbafb0a7..6d2ecac3188 100644 --- a/tests/swoole_coroutine/call_not_exists_func.phpt +++ b/tests/swoole_coroutine/call_not_exists_func.phpt @@ -15,13 +15,13 @@ $pm->parentFunc = function (int $pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { none(); $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); throw new Exception('whoops'); }); @@ -33,9 +33,9 @@ $pm->run(); --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function none() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d [%s] ERROR php_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Error: Call to undefined function none() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/call_user_func_array2.phpt b/tests/swoole_coroutine/call_user_func_array2.phpt index 7a50e1d002c..0a2ae437b80 100644 --- a/tests/swoole_coroutine/call_user_func_array2.phpt +++ b/tests/swoole_coroutine/call_user_func_array2.phpt @@ -25,7 +25,7 @@ go(function () { $result = call_user_func_array([$a, 'bar'], []); Assert::same($result, gethostbyname('www.tsinghua.edu.cn')); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- bar diff --git a/tests/swoole_coroutine/cancel/wait.phpt b/tests/swoole_coroutine/cancel/wait.phpt index a847f978e4c..75b24b78723 100644 --- a/tests/swoole_coroutine/cancel/wait.phpt +++ b/tests/swoole_coroutine/cancel/wait.phpt @@ -6,19 +6,27 @@ swoole_coroutine/cancel: wait/waitpid start(); + +run(function () use ($proc) { $cid = Coroutine::getCid(); go(function () use ($cid) { System::sleep(0.002); Assert::true(Coroutine::cancel($cid)); }); + $retval = System::wait(); echo "Done\n"; + Process::kill($proc->pid, SIGKILL); Assert::eq($retval, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED); }); diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 0c4e0b3432c..ab166aaee93 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -29,18 +29,6 @@ $map = [ (new Chan)->pop(); Assert::assert(0); // never here }, - function () { - Co::fread(STDIN); - Assert::assert(0); // never here - }, - function () { - Co::fgets(fopen(__FILE__, 'r')); - Assert::assert(0); // never here - }, - function () { - Co::fwrite(fopen(TEST_LOG_FILE, 'w+'), 'foo'); - Assert::assert(0); // never here - }, function () { Co::readFile(__FILE__); Assert::assert(0); // never here @@ -80,32 +68,8 @@ $map = [ (new Co\Http\Client('127.0.0.1', 1234))->get('/'); Assert::assert(0); // never here }, - function () { - (new Co\Mysql)->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert(0); // never here - }, - function () { - (new Co\Redis)->connect('127.0.0.1', 6379); - Assert::assert(0); // never here - }, ]; -function pgsql_test() { - (new Co\Postgresql())->connect('host=127.0.0.1 port=12345 dbname=test user=root password=root'); - Assert::assert(0); // never here -} - -if (class_exists(Co\Postgresql::class)) { - $map[] = function () { - pgsql_test(); - }; -} if (class_exists(Co\Http2\Client::class)) { $map[] = function () { (new Co\Http2\Client('127.0.0.1', 1234))->connect(); @@ -146,9 +110,16 @@ foreach ($map as $i => $f) { $process::wait(); if (Assert::contains($info, 'Swoole\\Error')) { $_info = trim($info); - $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); - $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); - $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + if (PHP_VERSION_ID >= 80400) { + $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); + $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); + $_info = preg_replace('/closure:[^(:]+:?\(?\d+\)?/', 'closure', $_info); + $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + } else { + $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); + $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); + $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + } $info_list[] = $_info; if (!Assert::assert($info_list[0] === $_info)) { var_dump($map[$i]); diff --git a/tests/swoole_coroutine/create_after_rshutdown.phpt b/tests/swoole_coroutine/create_after_rshutdown.phpt index 21062fa02d0..f35459ca6ad 100644 --- a/tests/swoole_coroutine/create_after_rshutdown.phpt +++ b/tests/swoole_coroutine/create_after_rshutdown.phpt @@ -10,7 +10,7 @@ register_shutdown_function(function () { co::sleep(.01); echo "DONE\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }); exit(0); diff --git a/tests/swoole_coroutine/defer/defer.phpt b/tests/swoole_coroutine/defer/defer.phpt index 61be922be74..7797b6b1a0f 100644 --- a/tests/swoole_coroutine/defer/defer.phpt +++ b/tests/swoole_coroutine/defer/defer.phpt @@ -61,7 +61,7 @@ go(function () { echo "3\n"; }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- 0 diff --git a/tests/swoole_coroutine/defer/defer_close.phpt b/tests/swoole_coroutine/defer/defer_close.phpt index 43e4cc3bd59..b5eaaf6cfff 100644 --- a/tests/swoole_coroutine/defer/defer_close.phpt +++ b/tests/swoole_coroutine/defer/defer_close.phpt @@ -28,12 +28,12 @@ go(function () { throw new Exception('something wrong'); echo "never here\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- -closed Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d +closed diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index 6bc982c66f8..74491f485f2 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -19,7 +19,7 @@ go(function () { defer(function () use ($foo, &$bar) { echo "defer 2\n"; Assert::same($foo, 2); - Assert::assert($foo !== 'gua'); // because of & + Assert::assert($bar !== 'gua'); // because of & }); $foo = 3; $bar = 'zha'; @@ -28,15 +28,15 @@ go(function () { echo "never here\n"; }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- 3 zha -defer 2 -defer 1 Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: -#0 {main} +%A thrown in %s/tests/swoole_coroutine/defer/defer_exception.php on line %d +defer 2 +defer 1 diff --git a/tests/swoole_coroutine/destruct/destruct1.phpt b/tests/swoole_coroutine/destruct/destruct1.phpt index 338f26ea5e7..bed26cec7cf 100644 --- a/tests/swoole_coroutine/destruct/destruct1.phpt +++ b/tests/swoole_coroutine/destruct/destruct1.phpt @@ -7,7 +7,7 @@ swoole_coroutine/destruct: destruct1 require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Coroutine as co; -class T +class T1 { function __construct() { @@ -28,7 +28,7 @@ class T } } -$t = new T(); +$t = new T1(); $t->test(); unset($t); echo "end\n"; diff --git a/tests/swoole_coroutine/destruct/destruct2.phpt b/tests/swoole_coroutine/destruct/destruct2.phpt index ed1fe5a7b06..a5ea0ca8c9d 100644 --- a/tests/swoole_coroutine/destruct/destruct2.phpt +++ b/tests/swoole_coroutine/destruct/destruct2.phpt @@ -7,7 +7,7 @@ swoole_coroutine/destruct: destruct2 require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Coroutine as co; -class T +class T2 { function __construct() { @@ -28,7 +28,7 @@ class T } } -$t = new T(); +$t = new T2(); $t->test(); echo "end\n"; ?> diff --git a/tests/swoole_coroutine/destruct/destruct3.phpt b/tests/swoole_coroutine/destruct/destruct3.phpt new file mode 100644 index 00000000000..e3f16249869 --- /dev/null +++ b/tests/swoole_coroutine/destruct/destruct3.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_coroutine/destruct: destruct 3 +--SKIPIF-- + +--FILE-- +dtor(); + } +}; + +Co\go(function () use ($t) { + Co::sleep(0.01); + $t->test(); + $GLOBALS['obj'] = $t; +}); +Swoole\Event::wait(); +?> +--EXPECTF-- +test +dtor + +Fatal error: Uncaught Exception: error in %s:%d +Stack trace: +#0 [internal function]: class@anonymous->{closure%S() +#1 {main} + thrown in %s on line %d diff --git a/tests/swoole_coroutine/dnslookup_3.phpt b/tests/swoole_coroutine/dnslookup_3.phpt index 1f6c343d1a9..bf1c23f3532 100644 --- a/tests/swoole_coroutine/dnslookup_3.phpt +++ b/tests/swoole_coroutine/dnslookup_3.phpt @@ -11,6 +11,6 @@ go(function () { Assert::eq($host, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine/exception.phpt b/tests/swoole_coroutine/exception.phpt index f7180aa1e17..1cff04de862 100644 --- a/tests/swoole_coroutine/exception.phpt +++ b/tests/swoole_coroutine/exception.phpt @@ -13,12 +13,12 @@ $pm->parentFunc = function (int $pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); throw new Exception('whoops'); }); @@ -30,9 +30,9 @@ $pm->run(); --EXPECTF-- Fatal error: Uncaught Exception: whoops in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d [%s] ERROR php_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Exception: whoops in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/exception/core_error.phpt b/tests/swoole_coroutine/exception/core_error.phpt new file mode 100644 index 00000000000..5586f1f9610 --- /dev/null +++ b/tests/swoole_coroutine/exception/core_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_coroutine/exception: fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected identifier "xde" in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/exception/defer1.phpt b/tests/swoole_coroutine/exception/defer1.phpt new file mode 100644 index 00000000000..65d41731257 --- /dev/null +++ b/tests/swoole_coroutine/exception/defer1.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_coroutine/exception: defer 1 +--SKIPIF-- + +--FILE-- +start(); +$status = Process::wait(); +if (Assert::isArray($status)) { + list($pid, $code, $signal) = array_values($status); + Assert::greaterThan($pid, 0); + + $out = $process->read(); + Assert::contains($out, 'co-1 begin'); + Assert::contains($out, 'co-2 begin'); + Assert::contains($out, 'defer task begin'); + Assert::contains($out, 'shutdown'); + Assert::contains($out, 'Fatal error: Uncaught Exception'); + Assert::notContains($out, 'co-1 end'); + Assert::same($code, 255); +} +?> +--EXPECTF-- diff --git a/tests/swoole_coroutine/exception/defer2.phpt b/tests/swoole_coroutine/exception/defer2.phpt new file mode 100644 index 00000000000..865f9cf31b7 --- /dev/null +++ b/tests/swoole_coroutine/exception/defer2.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_coroutine/exception: defer 2 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +co-1 begin +co-2 begin +co-2 end +defer task begin + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 [internal function]: {closure%S}(NULL) +#1 {main} + thrown in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/exception/dtor.phpt b/tests/swoole_coroutine/exception/dtor.phpt new file mode 100644 index 00000000000..24a99b35de5 --- /dev/null +++ b/tests/swoole_coroutine/exception/dtor.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_coroutine/exception: throw in destructor +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +co-1 begin +test + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): class@anonymous->__destruct() +%A + thrown in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/exception/error.phpt b/tests/swoole_coroutine/exception/error.phpt index b9cefc0b6a1..2411dd70f1a 100644 --- a/tests/swoole_coroutine/exception/error.phpt +++ b/tests/swoole_coroutine/exception/error.phpt @@ -5,10 +5,13 @@ swoole_coroutine/exception: error --FILE-- +--FILE-- + 1, + 'location' => $wsdl, + 'features' => 1, + 'exceptions' => true, + ], []); + $client = new \SoapClient($wsdl, $option); + } catch (\Exception $e) { + echo $e->getMessage(); + } +}); +echo "end\n"; + +?> +--EXPECTF-- +start +SOAP-ERROR: Parsing WSDL: Couldn't load from '%s' : failed to load external entity "%s" +end diff --git a/tests/swoole_coroutine/exception/error3.phpt b/tests/swoole_coroutine/exception/error3.phpt new file mode 100644 index 00000000000..af9c99b19e3 --- /dev/null +++ b/tests/swoole_coroutine/exception/error3.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_coroutine/exception: internal_function error +--SKIPIF-- + +--FILE-- + +--FILE-- +start(); +$status = Process::wait(); +if (Assert::isArray($status)) { + list($pid, $code, $signal) = array_values($status); + Assert::greaterThan($pid, 0); + + $out = $process->read(); + Assert::contains($out, 'Uncaught Error: Call to undefined function call_func_not_exists()'); + Assert::contains($out, 'shutdown'); + Assert::notContains($out, 'co end'); + Assert::same($code, 255); +} +?> +--EXPECT-- diff --git a/tests/swoole_coroutine/execute_time.phpt b/tests/swoole_coroutine/execute_time.phpt new file mode 100644 index 00000000000..67ddb5c7ae3 --- /dev/null +++ b/tests/swoole_coroutine/execute_time.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_coroutine: getExecuteTime +--SKIPIF-- + +--FILE-- + 0) { + $a = 9999 ^ 10000; + $i--; + } + $execution_time = Swoole\Coroutine::getExecuteTime(); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + echo 'DONE'; +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine/exit.phpt b/tests/swoole_coroutine/exit.phpt index a8f83140212..50d710f449b 100644 --- a/tests/swoole_coroutine/exit.phpt +++ b/tests/swoole_coroutine/exit.phpt @@ -1,75 +1,13 @@ --TEST-- swoole_coroutine: exit --SKIPIF-- - + --FILE-- 'ok'], - (object)['exit' => 'ok'], - STDIN, - 0 -]; - -function route() -{ - controller(); -} - -function controller() -{ - your_code(); -} - -function your_code() -{ - global $exit_status_list; - co::sleep(.001); - $exit_status = array_shift($exit_status_list); - if ($exit_status === 'undef') { - exit; - } else { - exit($exit_status); - } -} - -$chan = new Swoole\Coroutine\Channel; - -go(function () use ($chan, $exit_status_list) { - foreach ($exit_status_list as $val) { - $chan->push($val); - } -}); - -for ($i = 0; $i < count($exit_status_list); $i++) { - go(function () use ($exit_status_list, $chan) { - try { - // in coroutine - route(); - } catch (\Swoole\ExitException $e) { - Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE); - $exit_status = $chan->pop(); - $exit_status = $exit_status === 'undef' ? null : $exit_status; - Assert::same($e->getStatus(), $exit_status); - var_dump($e->getStatus()); - // exit coroutine - return; - } - echo "never here\n"; - }); -} - -swoole_event::wait(); - +require TESTS_API_PATH . '/exit.php'; ?> --EXPECTF-- NULL diff --git a/tests/swoole_coroutine/exit_84.phpt b/tests/swoole_coroutine/exit_84.phpt new file mode 100644 index 00000000000..9abfb9ea105 --- /dev/null +++ b/tests/swoole_coroutine/exit_84.phpt @@ -0,0 +1,15 @@ +--TEST-- +swoole_coroutine: exit +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +int(1) +string(4) "exit" +int(0) diff --git a/tests/swoole_coroutine/exit_exception_backtrace.phpt b/tests/swoole_coroutine/exit_exception_backtrace.phpt index e60d4f1bfae..66007ed34dd 100644 --- a/tests/swoole_coroutine/exit_exception_backtrace.phpt +++ b/tests/swoole_coroutine/exit_exception_backtrace.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_coroutine: exit exception backtrace --SKIPIF-- - + --FILE-- --EXPECTF-- -Fatal error: Uncaught Swoole\ExitException: swoole exit in %s/tests/swoole_coroutine/exit_exception_backtrace.php:15 +Fatal error: Uncaught Swoole\ExitException: swoole exit in %s/tests/swoole_coroutine/exit_exception_backtrace.php:%d Stack trace: -#0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(10): char(%d) -#1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(5): bar('%s...') -#2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(22): foo() -#3 {main} - thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line 15 +#0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): char(%d) +#1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): bar('%s...') +#2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): foo() +%A + thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line %d diff --git a/tests/swoole_coroutine/exit_exception_backtrace_84.phpt b/tests/swoole_coroutine/exit_exception_backtrace_84.phpt new file mode 100644 index 00000000000..72e13edfa67 --- /dev/null +++ b/tests/swoole_coroutine/exit_exception_backtrace_84.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_coroutine: exit exception backtrace +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Swoole\ExitException: swoole exit in %s:%d +Stack trace: +#0 %s(%d): exit() +#1 %s(%d): char(%d) +#2 %s(%d): bar('%s') +#3 %s(%d): foo() +#4 [internal function]: {closure:%s:%d}() +#5 {main} + thrown in %s on line %d + diff --git a/tests/swoole_coroutine/fatal_error.phpt b/tests/swoole_coroutine/fatal_error.phpt index 8437bb21833..472edc3f612 100644 --- a/tests/swoole_coroutine/fatal_error.phpt +++ b/tests/swoole_coroutine/fatal_error.phpt @@ -10,6 +10,9 @@ Co::set([ 'enable_deadlock_check' => true, ]); +register_shutdown_function(function () { + echo "shutdown\n"; +}); Co\run(function () { test_not_found(); @@ -19,5 +22,6 @@ echo "DONE\n"; --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function test_not_found() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/gethostbyname.phpt b/tests/swoole_coroutine/gethostbyname.phpt index 3cddd73a03b..655804d02f8 100644 --- a/tests/swoole_coroutine/gethostbyname.phpt +++ b/tests/swoole_coroutine/gethostbyname.phpt @@ -15,7 +15,7 @@ use Swoole\Coroutine; use function Swoole\Coroutine\run; run(function () { - $map = IS_IN_TRAVIS ? [ + $map = IS_IN_CI ? [ 'www.google.com' => null, 'www.youtube.com' => null, 'www.facebook.com' => null, @@ -69,7 +69,7 @@ run(function () { $no_cache_multi_time = microtime(true) - $no_cache_multi_time; phpt_var_dump($first_time, $cache_time, $no_cache_time, $no_cache_multi_time); - if (!IS_IN_TRAVIS) { + if (!IS_IN_CI) { Assert::assert($cache_time < 0.01); Assert::assert($cache_time < $first_time); Assert::assert($cache_time < $no_cache_time); diff --git a/tests/swoole_coroutine/join/3.phpt b/tests/swoole_coroutine/join/3.phpt index 5e99872f4f4..06f390fdbc1 100644 --- a/tests/swoole_coroutine/join/3.phpt +++ b/tests/swoole_coroutine/join/3.phpt @@ -23,7 +23,7 @@ run(function () { Assert::eq(swoole_last_error(), SWOOLE_ERROR_WRONG_OPERATION); Assert::false(Coroutine::join([9999])); - Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_NOT_EXISTS); + Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); $cid_list = []; $cid_list[] = go(function () { diff --git a/tests/swoole_coroutine/join/4.phpt b/tests/swoole_coroutine/join/4.phpt index 5ab8d152639..c9d00029883 100644 --- a/tests/swoole_coroutine/join/4.phpt +++ b/tests/swoole_coroutine/join/4.phpt @@ -15,16 +15,18 @@ ini_set('swoole.display_errors', 'off'); run(function () { $current_cid = Coroutine::getCid(); - $cid_list[] = go(function () use ($current_cid) { + $cid = go(function () use ($current_cid) { System::sleep(.1); - swoole_event_defer(function () use ($current_cid) { + Swoole\Event::defer(function () use ($current_cid) { echo "DEFER CALLBACK\n"; Coroutine::cancel($current_cid); }); }); + $cid_list[] = $cid; Assert::false(Coroutine::join($cid_list)); Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED); + Assert::false(Coroutine::exists($cid)); echo "DONE\n"; }); ?> diff --git a/tests/swoole_coroutine/join/5.phpt b/tests/swoole_coroutine/join/5.phpt new file mode 100644 index 00000000000..8896cae9b88 --- /dev/null +++ b/tests/swoole_coroutine/join/5.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_coroutine/join: 5 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE 2 +DONE 1 diff --git a/tests/swoole_coroutine/join/6.phpt b/tests/swoole_coroutine/join/6.phpt new file mode 100644 index 00000000000..f754e0c48bd --- /dev/null +++ b/tests/swoole_coroutine/join/6.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/join: 6 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine/join/7.phpt b/tests/swoole_coroutine/join/7.phpt new file mode 100644 index 00000000000..a292dbc0da4 --- /dev/null +++ b/tests/swoole_coroutine/join/7.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_coroutine/join: 7 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DEFER CALLBACK +DONE diff --git a/tests/swoole_coroutine/join/8.phpt b/tests/swoole_coroutine/join/8.phpt new file mode 100644 index 00000000000..734ea0dedfa --- /dev/null +++ b/tests/swoole_coroutine/join/8.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_coroutine/join: 8 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine/list_and_backtrace.phpt b/tests/swoole_coroutine/list_and_backtrace.phpt index 034744e5078..47516ce4957 100644 --- a/tests/swoole_coroutine/list_and_backtrace.phpt +++ b/tests/swoole_coroutine/list_and_backtrace.phpt @@ -35,193 +35,42 @@ go(function () { }); }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- int(1) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(31) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (0) { - } - } - } +array(%d) { + %A } int(2) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(30) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (0) { - } - } - } +array(%d) { + %A } int(3) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(29) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (1) { - ["static"]=> - array(1) { - ["main"]=> - int(4) - } - } - } - } +array(%d) { + %A } int(4) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(13) - ["function"]=> - string(12) "getBackTrace" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - int(4) - } - } +array(%d) { + %A } int(5) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(28) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (1) { - ["static"]=> - array(1) { - ["main"]=> - int(4) - } - } - } - } +array(%d) { + %A } int(6) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(18) - ["function"]=> - string(5) "sleep" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - float(0.001) - } - } +array(%d) { + %A } int(7) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(21) - ["function"]=> - string(8) "readFile" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - string(%d) "%s" - } - } +array(%d) { + %A } int(8) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(24) - ["function"]=> - string(11) "getaddrinfo" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - string(9) "localhost" - } - } +array(%d) { + %A } int(9) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(27) - ["function"]=> - string(6) "resume" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - int(4) - } - } +array(%d) { + %A } diff --git a/tests/swoole_coroutine/new_process.phpt b/tests/swoole_coroutine/new_process.phpt index 15764fa41ab..07534aee657 100644 --- a/tests/swoole_coroutine/new_process.phpt +++ b/tests/swoole_coroutine/new_process.phpt @@ -14,5 +14,5 @@ go(function () { Fatal error: Uncaught Swoole\Error: must be forked outside the coroutine in %s:%d Stack trace: #0 %s(5): Swoole\Process->start() -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/new_server.phpt b/tests/swoole_coroutine/new_server.phpt index b9e1c3415e9..926771363f5 100644 --- a/tests/swoole_coroutine/new_server.phpt +++ b/tests/swoole_coroutine/new_server.phpt @@ -6,8 +6,9 @@ swoole_coroutine: new server on('receive', function () { }); + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_PROCESS); + $server->on('receive', function () { + }); $server->start(); }); ?> diff --git a/tests/swoole_coroutine/output/in_nested_co.phpt b/tests/swoole_coroutine/output/in_nested_co.phpt index 2ac4e787d10..f43e8cb315b 100644 --- a/tests/swoole_coroutine/output/in_nested_co.phpt +++ b/tests/swoole_coroutine/output/in_nested_co.phpt @@ -5,19 +5,18 @@ swoole_coroutine/output: use ob_* in nest co --FILE-- --EXPECT-- 1 diff --git a/tests/swoole_coroutine/output/ob_main.phpt b/tests/swoole_coroutine/output/ob_main.phpt index 10f93809601..c19bb9f22b3 100644 --- a/tests/swoole_coroutine/output/ob_main.phpt +++ b/tests/swoole_coroutine/output/ob_main.phpt @@ -7,10 +7,10 @@ swoole_coroutine/output: main output global require __DIR__ . '/../../include/bootstrap.php'; ob_start(); echo 'aaa'; -go(function () { +Co\run(function () { ob_start(); echo 'bbb'; - co::fgets(fopen(__FILE__, 'r')); + fgets(fopen(__FILE__, 'r')); Assert::same(ob_get_clean(), 'bbb'); }); Assert::same(ob_get_clean(), 'aaa'); diff --git a/tests/swoole_coroutine/pdo_error_handing.phpt b/tests/swoole_coroutine/pdo_error_handing.phpt index 25535397b9c..2b28c240729 100644 --- a/tests/swoole_coroutine/pdo_error_handing.phpt +++ b/tests/swoole_coroutine/pdo_error_handing.phpt @@ -23,4 +23,4 @@ Swoole\Event::wait(); ?> --EXPECTF-- -Warning: fopen(%s): failed to open stream: No such file or directory in %s on line %d +Warning: fopen(%s): %s to open stream: No such file or directory in %s on line %d diff --git a/tests/swoole_coroutine/private_access.phpt b/tests/swoole_coroutine/private_access.phpt index 143fcdd3fc7..bfe4f9d1dcc 100644 --- a/tests/swoole_coroutine/private_access.phpt +++ b/tests/swoole_coroutine/private_access.phpt @@ -19,6 +19,8 @@ class Bar public function foo() { + \Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + go(function () { var_dump(self::$s_private); var_dump(self::$s_protect); @@ -47,16 +49,16 @@ class Bar var_dump($this->private); var_dump($this->protect); var_dump($this->public); - $mysql = new Swoole\Coroutine\MySQL; - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $res = $mysql->connect( + MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT, + ); Assert::assert($res); - $ret = $mysql->query('show tables', 1); + $ret = $mysql->query('show tables', 1)->fetch_all(); Assert::assert(is_array($ret)); Assert::assert(count($ret) > 0); var_dump(self::$s_private); diff --git a/tests/swoole_coroutine/signal_listener.phpt b/tests/swoole_coroutine/signal_listener.phpt index e8132e4d95a..567b17664c8 100644 --- a/tests/swoole_coroutine/signal_listener.phpt +++ b/tests/swoole_coroutine/signal_listener.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_coroutine: dead lock +swoole_coroutine: signal listener --SKIPIF-- --FILE-- @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine; use Swoole\Process; -ini_set('swoole.enable_coroutine', 'off'); +swoole_async_set(['enable_coroutine' => false]); $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { @@ -27,12 +27,9 @@ $pm->childFunc = function () use ($pm) { return Coroutine::stats()['signal_listener_num'] === 0; } ]); - Process::signal(SIGINT, function () { - echo 'SIGINT' . PHP_EOL; - exit(123); - }); Process::signal(SIGTERM, function () { echo 'SIGTERM' . PHP_EOL; + Process::signal(SIGTERM, null); exit(123); }); }; diff --git a/tests/swoole_coroutine/use_process.phpt b/tests/swoole_coroutine/use_process.phpt index 19b90b39f9c..dbde4373fc5 100644 --- a/tests/swoole_coroutine/use_process.phpt +++ b/tests/swoole_coroutine/use_process.phpt @@ -11,7 +11,7 @@ $pm = new SwooleTest\ProcessManager(); const SIZE = 8192 * 5; const TIMES = 10; $pm->parentFunc = function () use ($pm) { - $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ "open_eof_check" => true, "package_eof" => "\r\n\r\n" @@ -31,13 +31,13 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new \swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null' ]); - $proc = new swoole\process(function ($process) use ($serv) { + $proc = new Swoole\Process(function ($process) use ($serv) { $data = json_decode($process->read(), true); for ($i = 0; $i < TIMES/2; $i ++) { go (function() use ($serv,$data, $i){ @@ -51,10 +51,10 @@ $pm->childFunc = function () use ($pm) { }, false, true); $serv->addProcess($proc); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $reactorId, $data) use ($proc) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $reactorId, $data) use ($proc) { $proc->write(json_encode([ 'fd' => $fd ])); diff --git a/tests/swoole_coroutine/user_coroutine.phpt b/tests/swoole_coroutine/user_coroutine.phpt index b1705ef131d..9ce089174b6 100644 --- a/tests/swoole_coroutine/user_coroutine.phpt +++ b/tests/swoole_coroutine/user_coroutine.phpt @@ -3,7 +3,7 @@ swoole_coroutine: user coroutine --SKIPIF-- --FILE-- @@ -23,11 +23,11 @@ $pm->parentFunc = function () use ($pm) }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Swoole\Coroutine::create(function () use ($response) { diff --git a/tests/swoole_coroutine_lock/lock.phpt b/tests/swoole_coroutine_lock/lock.phpt new file mode 100644 index 00000000000..a7ebc51f25c --- /dev/null +++ b/tests/swoole_coroutine_lock/lock.phpt @@ -0,0 +1,106 @@ +--TEST-- +swoole_coroutine_lock: lock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function ($pid) use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + run(function () use ($pm) { + $waitGroup = new WaitGroup(); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=1", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=2", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=3", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + + $waitGroup->wait(); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + swoole_async_set([ + 'log_file' => '/dev/null', + ]); + $lock = new Lock(true); + Assert::false($lock->lock()); + Assert::false($lock->unlock()); + Assert::eq($lock->errCode, SWOOLE_ERROR_CO_OUT_OF_COROUTINE); + $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 4, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL + ]); + + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($lock) { + $resp->header('Content-Type', 'text/plain'); + if ($req->get['value'] == 1 || $req->get['value'] == 2) { + $lock->lock(); + if ($req->get['value'] == 1) { + sleep(1); + } + $resp->end(json_encode(['result' => 'lock' . $req->get['value']]) . PHP_EOL); + $lock->unlock(); + } else { + $resp->end(json_encode(['result' => 'value 3']) . PHP_EOL); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(1) { + ["result"]=> + string(7) "value 3" +} +array(1) { + ["result"]=> + string(5) "lock1" +} +array(1) { + ["result"]=> + string(5) "lock2" +} +DONE diff --git a/tests/swoole_coroutine_lock/trylock.phpt b/tests/swoole_coroutine_lock/trylock.phpt new file mode 100644 index 00000000000..3e418feb684 --- /dev/null +++ b/tests/swoole_coroutine_lock/trylock.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_coroutine_lock: trylock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +run(function () { + $lock = new Lock(false); + Assert::eq($lock->trylock(), true); + go(function () use ($lock) { + Assert::eq($lock->trylock(), false); + $s = microtime(true); + Assert::eq($lock->lock(), true); + Assert::assert(microtime(true) - $s >= 0.05); + echo "co2 end\n"; + }); + + System::sleep(0.05); + Assert::eq($lock->unlock(), true); + echo "co1 end\n"; +}); +echo "DONE\n"; +?> +--EXPECT-- +co1 end +co2 end +DONE diff --git a/tests/swoole_coroutine_lock/trylock2.phpt b/tests/swoole_coroutine_lock/trylock2.phpt new file mode 100644 index 00000000000..35a7f070beb --- /dev/null +++ b/tests/swoole_coroutine_lock/trylock2.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_lock: coroutine try lock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +$lock = new Lock(false); + +run(function () use ($argv, $lock) { + $waitGroup = new WaitGroup(); + go(function () use ($waitGroup, $lock) { + $waitGroup->add(); + $lock->lock(); + sleep(2); + var_dump(1); + $lock->unlock(); + $waitGroup->done(); + }); + + go(function () use ($waitGroup, $lock) { + $waitGroup->add(); + if (!$lock->trylock()) { + var_dump('lock failed'); + } + $waitGroup->done(); + }); + + $waitGroup->wait(); +}); +?> +--EXPECTF-- +string(11) "lock failed" +int(1) diff --git a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt index b2eb446cdce..3f69a95b738 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt @@ -45,7 +45,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt b/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt index d9f970a12a6..a32378b67cd 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt b/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt index 156af6c7dbd..501d5eeb28b 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/for.phpt b/tests/swoole_coroutine_scheduler/preemptive/for.phpt index c60d8579388..040efe32b86 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/for.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/for.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/for2.phpt b/tests/swoole_coroutine_scheduler/preemptive/for2.phpt index ed461cb3048..0313e347acf 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/for2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/for2.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/goto.phpt b/tests/swoole_coroutine_scheduler/preemptive/goto.phpt index 83b803f3c5f..e28c3e70a31 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/goto.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/goto.phpt @@ -36,7 +36,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt b/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt index 9d9656b67ed..77f9ec9798e 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while.phpt b/tests/swoole_coroutine_scheduler/preemptive/while.phpt index 68b7f181fc7..da7f7cf27ae 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while.phpt @@ -34,7 +34,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while2.phpt b/tests/swoole_coroutine_scheduler/preemptive/while2.phpt index 0c27c49015c..842e84dfab5 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while2.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while3.phpt b/tests/swoole_coroutine_scheduler/preemptive/while3.phpt index c2014595fec..288187669c5 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while3.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while3.phpt @@ -34,7 +34,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/repeat.phpt b/tests/swoole_coroutine_scheduler/repeat.phpt new file mode 100644 index 00000000000..29602fa7cdd --- /dev/null +++ b/tests/swoole_coroutine_scheduler/repeat.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine_scheduler: user yield and resume1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +scheduler 1: begin +scheduler 1: end +sleep: begin +sleep: end +scheduler 2: begin +scheduler 2: end +DONE diff --git a/tests/swoole_coroutine_scheduler/resume1.phpt b/tests/swoole_coroutine_scheduler/resume1.phpt index 956f460b2e9..dbf78db368b 100644 --- a/tests/swoole_coroutine_scheduler/resume1.phpt +++ b/tests/swoole_coroutine_scheduler/resume1.phpt @@ -21,7 +21,7 @@ co::resume($id); echo "start to resume $id @2\n"; co::resume($id); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume2.phpt b/tests/swoole_coroutine_scheduler/resume2.phpt index a1a95d16347..c5aa983937b 100644 --- a/tests/swoole_coroutine_scheduler/resume2.phpt +++ b/tests/swoole_coroutine_scheduler/resume2.phpt @@ -28,7 +28,7 @@ co::resume($map[2]); echo "start to resume {$map[1]}\n"; co::resume($map[1]); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume3.phpt b/tests/swoole_coroutine_scheduler/resume3.phpt index b3798771f68..6c75c3bb26e 100644 --- a/tests/swoole_coroutine_scheduler/resume3.phpt +++ b/tests/swoole_coroutine_scheduler/resume3.phpt @@ -28,7 +28,7 @@ co::resume($map[1]); echo "start to resume {$map[2]}\n"; co::resume($map[2]); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume4.phpt b/tests/swoole_coroutine_scheduler/resume4.phpt index b906a980e7a..f73f825a853 100644 --- a/tests/swoole_coroutine_scheduler/resume4.phpt +++ b/tests/swoole_coroutine_scheduler/resume4.phpt @@ -15,7 +15,7 @@ $id = go(function () { echo "start to resume $id\n"; Co::resume($id); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- Fatal error: Uncaught Swoole\Error: API must be called in the coroutine in %s:%d diff --git a/tests/swoole_coroutine_scheduler/resume5.phpt b/tests/swoole_coroutine_scheduler/resume5.phpt index 3b5f46fa253..afade3f6d6f 100644 --- a/tests/swoole_coroutine_scheduler/resume5.phpt +++ b/tests/swoole_coroutine_scheduler/resume5.phpt @@ -22,7 +22,7 @@ go(function () { echo "after yield\n"; }); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start to create coro diff --git a/tests/swoole_coroutine_scheduler/resume6.phpt b/tests/swoole_coroutine_scheduler/resume6.phpt index e54403eb7bc..92b81efb3fb 100644 --- a/tests/swoole_coroutine_scheduler/resume6.phpt +++ b/tests/swoole_coroutine_scheduler/resume6.phpt @@ -20,7 +20,7 @@ go(function () { echo "coro 2 end\n"; }); echo "main 2\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- coro 1 start diff --git a/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt b/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt index f2e9b9d52e2..3c5bb1d25f9 100644 --- a/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt +++ b/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt @@ -6,6 +6,8 @@ swoole_coroutine_scheduler: start in server onShutdown callback set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($file) { if ($worker_id == 1) { - $server->after(200, function () use ($server, $file) { - Co::sleep(0.1); + Swoole\Timer::after(200, function () use ($server, $file) { + System::sleep(0.1); echo "[1] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); $server->shutdown(); }); } @@ -28,9 +30,9 @@ $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) $server->on('shutdown', function () use ($file) { $sch = new Co\Scheduler; $sch->add(function ($t, $n) use ($file) { - Co::sleep($t); + System::sleep($t); echo "[2] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); }, 0.05, 'A'); $sch->start(); }); diff --git a/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt b/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt index 96866d5690a..29476dfd052 100644 --- a/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt +++ b/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt @@ -6,6 +6,8 @@ swoole_coroutine_scheduler: start in server onShutdown callback set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($file) { if ($worker_id == 1) { - $server->after(200, function () use ($server, $file) { - Co::sleep(0.1); + Swoole\Timer::after(200, function () use ($server, $file) { + System::sleep(0.1); echo "[1] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); $server->shutdown(); }); } @@ -29,9 +31,9 @@ $server->on('workerStop', function (Swoole\Server $server, int $worker_id) use ( if ($worker_id == 1) { $sch = new Co\Scheduler; $sch->add(function ($t, $n) use ($file) { - Co::sleep($t); + System::sleep($t); echo "[2] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); }, 0.05, 'A'); $sch->start(); } diff --git a/tests/swoole_coroutine_system/fread.phpt b/tests/swoole_coroutine_system/fread.phpt deleted file mode 100644 index d73105aa21d..00000000000 --- a/tests/swoole_coroutine_system/fread.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_coroutine_system: fread ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_coroutine_system/fwrite.phpt b/tests/swoole_coroutine_system/fwrite.phpt deleted file mode 100644 index 1b25da5463e..00000000000 --- a/tests/swoole_coroutine_system/fwrite.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_coroutine_system: fwrite ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_coroutine_system/getaddrinfo.phpt b/tests/swoole_coroutine_system/getaddrinfo.phpt index 5fda59a59b8..87e91851607 100644 --- a/tests/swoole_coroutine_system/getaddrinfo.phpt +++ b/tests/swoole_coroutine_system/getaddrinfo.phpt @@ -7,7 +7,7 @@ skip_if_offline(); --FILE-- +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine_system/gethostbyname_timeout.phpt b/tests/swoole_coroutine_system/gethostbyname_timeout.phpt index b8787f445c3..1f781d07f06 100644 --- a/tests/swoole_coroutine_system/gethostbyname_timeout.phpt +++ b/tests/swoole_coroutine_system/gethostbyname_timeout.phpt @@ -17,7 +17,7 @@ Swoole\Coroutine::create(function () { $result = Swoole\Coroutine\System::gethostbyname("www.github.com", AF_INET, 1); Assert::notEmpty($result); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- NEXT diff --git a/tests/swoole_coroutine_system/waitSignal.phpt b/tests/swoole_coroutine_system/waitSignal.phpt index 9f424775b3c..44758850e31 100644 --- a/tests/swoole_coroutine_system/waitSignal.phpt +++ b/tests/swoole_coroutine_system/waitSignal.phpt @@ -32,13 +32,13 @@ Coroutine\run(function () use ($atomic) { switch_process(); $atomic->wakeup(); echo "1\n"; - Assert::true(System::waitSignal(SIGUSR1)); + Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1); echo "3\n"; Assert::false(System::waitSignal(SIGUSR2, 0.01)); echo "4\n"; $atomic->wakeup(); echo "5\n"; - Assert::true(System::waitSignal(SIGUSR2)); + Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2); echo "7\n"; System::wait(); echo "9\n"; diff --git a/tests/swoole_coroutine_system/waitSignal_2.phpt b/tests/swoole_coroutine_system/waitSignal_2.phpt new file mode 100644 index 00000000000..09d238d23fc --- /dev/null +++ b/tests/swoole_coroutine_system/waitSignal_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_coroutine_system: waitSignal 2 +--SKIPIF-- + +--FILE-- +wait(); + echo "2\n"; + switch_process(); + Process::kill($pid, SIGUSR1); + $atomic->wait(); + echo "6\n"; + switch_process(); + Process::kill($pid, SIGUSR2); + echo "8\n"; +}); +$killer->start(); + +Coroutine\run(function () use ($atomic) { + Coroutine::sleep(0.001); + switch_process(); + $atomic->wakeup(); + echo "1\n"; + $list = [SIGUSR1, SIGUSR2, SIGIO]; + Assert::eq(System::waitSignal($list), SIGUSR1); + echo "3\n"; + Assert::false(System::waitSignal($list, 0.01)); + echo "4\n"; + $atomic->wakeup(); + echo "5\n"; + Assert::eq(System::waitSignal($list), SIGUSR2); + echo "7\n"; + System::wait(); + echo "9\n"; +}); + +?> +--EXPECT-- +1 +2 +3 +4 +5 +6 +8 +7 +9 diff --git a/tests/swoole_coroutine_util/dns_lookup.phpt b/tests/swoole_coroutine_util/dns_lookup.phpt index 260020f38fc..0866550f21c 100644 --- a/tests/swoole_coroutine_util/dns_lookup.phpt +++ b/tests/swoole_coroutine_util/dns_lookup.phpt @@ -19,11 +19,11 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $host = swoole_async_dns_lookup_coro('www.baidu.com'); if ($host) diff --git a/tests/swoole_coroutine_util/exec_sleep.phpt b/tests/swoole_coroutine_util/exec_sleep.phpt index 1df145ae663..827b93730b1 100644 --- a/tests/swoole_coroutine_util/exec_sleep.phpt +++ b/tests/swoole_coroutine_util/exec_sleep.phpt @@ -12,7 +12,7 @@ for ($i = MAX_PROCESS_NUM; $i--;) { co::exec('sleep 1'); }); } -swoole_event_wait(); +Swoole\Event::wait(); $s = microtime(true) - $s; time_approximate(1, $s); echo "DONE\n"; diff --git a/tests/swoole_coroutine_util/fgets.phpt b/tests/swoole_coroutine_util/fgets.phpt index fff3f62a644..5e45518ef62 100644 --- a/tests/swoole_coroutine_util/fgets.phpt +++ b/tests/swoole_coroutine_util/fgets.phpt @@ -6,33 +6,24 @@ swoole_coroutine_util: fgets 0]); - Co\run(function () { - $file = __DIR__ . '/../../examples/server.php'; - - $coroutine = ''; - $fp = fopen($file, "r"); - while (!feof($fp)) { - $coroutine .= co::fgets($fp); - } + $file = __DIR__ . '/../../examples/server/mixed.php'; - $standard = ''; + Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + $coroutine = []; $fp = fopen($file, "r"); while (!feof($fp)) { - $standard .= fgets($fp); + $coroutine [] = fgets($fp); } - Swoole\Runtime::enableCoroutine(); - $runtime = ''; + Swoole\Runtime::enableCoroutine(false); + $standard = []; $fp = fopen($file, "r"); while (!feof($fp)) { - $runtime .= fgets($fp); + $standard [] = fgets($fp); } Assert::same($standard, $coroutine); - Assert::same($standard, $runtime); - echo "DONE\n"; }); ?> diff --git a/tests/swoole_coroutine_util/fread.phpt b/tests/swoole_coroutine_util/fread.phpt index 2f0d35153a0..ff069f7485c 100644 --- a/tests/swoole_coroutine_util/fread.phpt +++ b/tests/swoole_coroutine_util/fread.phpt @@ -5,21 +5,14 @@ swoole_coroutine_util: fread --FILE-- --EXPECT-- diff --git a/tests/swoole_coroutine_util/fread_seek.phpt b/tests/swoole_coroutine_util/fread_seek.phpt deleted file mode 100644 index 9e4025432b3..00000000000 --- a/tests/swoole_coroutine_util/fread_seek.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_coroutine_util: fread and fseek ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_coroutine_util/fwrite.phpt b/tests/swoole_coroutine_util/fwrite.phpt index b6eff6dfe13..ef7ce07b29a 100644 --- a/tests/swoole_coroutine_util/fwrite.phpt +++ b/tests/swoole_coroutine_util/fwrite.phpt @@ -5,27 +5,20 @@ swoole_coroutine_util: fwrite --FILE-- --EXPECT-- diff --git a/tests/swoole_coroutine_util/list_coroutine.phpt b/tests/swoole_coroutine_util/list_coroutine.phpt index 13df28a1cfb..ca0e13e58f4 100644 --- a/tests/swoole_coroutine_util/list_coroutine.phpt +++ b/tests/swoole_coroutine_util/list_coroutine.phpt @@ -21,7 +21,7 @@ go(function () use ($co_list) { Assert::same(array_values(array_diff($list_2, $co_list)), [Co::getUid(),]); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine_util/task_worker.phpt b/tests/swoole_coroutine_util/task_worker.phpt index 6e499d691e7..0c929f63474 100644 --- a/tests/swoole_coroutine_util/task_worker.phpt +++ b/tests/swoole_coroutine_util/task_worker.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); $cli->send("task-01") or die("ERROR"); @@ -19,28 +19,28 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, 'log_file' => '/dev/null', 'task_enable_coroutine' => true )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'sleep']); }); - $serv->on('task', function (swoole_server $serv, $task) { + $serv->on('task', function (Swoole\Server $serv, $task) { list($fd) = $task->data; co::sleep(0.2); $serv->send($fd, "sleep\r\n\r\n"); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_coroutine_wait_group/base.phpt b/tests/swoole_coroutine_wait_group/base.phpt index 54bc33c1b6f..b18be62845e 100644 --- a/tests/swoole_coroutine_wait_group/base.phpt +++ b/tests/swoole_coroutine_wait_group/base.phpt @@ -5,13 +5,17 @@ swoole_coroutine_wait_group: base --FILE-- add(); Assert::same( file_get_contents(__FILE__), - Co::readFile(__FILE__) + System::readFile(__FILE__) ); echo "TASK[1] DONE\n"; $wg->done(); diff --git a/tests/swoole_coroutine_wait_group/defer.phpt b/tests/swoole_coroutine_wait_group/defer.phpt new file mode 100644 index 00000000000..d40dc92a148 --- /dev/null +++ b/tests/swoole_coroutine_wait_group/defer.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_coroutine_wait_group: run in defer +--SKIPIF-- + +--FILE-- +add(); + Co\go(function () use ($wg, $i) { + var_dump("start $i"); + defer(function () use ($wg, $i) { + var_dump("defer $i"); + $wg->done(); + var_dump("done $i"); + }); + var_dump("end $i"); + }); + } + + var_dump("wait"); + $wg->wait(); + var_dump("finish"); +}); + +?> +--EXPECT-- +string(5) "add 1" +string(7) "start 1" +string(5) "end 1" +string(7) "defer 1" +string(6) "done 1" +string(5) "add 2" +string(7) "start 2" +string(5) "end 2" +string(7) "defer 2" +string(6) "done 2" +string(4) "wait" +string(6) "finish" diff --git a/tests/swoole_curl/basic/10.phpt b/tests/swoole_curl/basic/10.phpt index a99aa7248f7..ed748798ce0 100644 --- a/tests/swoole_curl/basic/10.phpt +++ b/tests/swoole_curl/basic/10.phpt @@ -23,13 +23,11 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_URL, $url); curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); + Assert::eq(curl_errno($ch), CURLE_COULDNT_RESOLVE_PROXY); curl_close($ch); - + echo "DONE\n"; }, false); ?> ---EXPECTF-- -string(%d) "%r(Couldn't resolve proxy|Could not resolve proxy:|Could not resolve host:|Could not resolve:)%r %s" -int(5) +--EXPECT-- +DONE diff --git a/tests/swoole_curl/cancel.phpt b/tests/swoole_curl/cancel.phpt index 1f8f998abae..1a6370eb742 100644 --- a/tests/swoole_curl/cancel.phpt +++ b/tests/swoole_curl/cancel.phpt @@ -48,7 +48,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/close_before_resume.phpt b/tests/swoole_curl/close_before_resume.phpt index 76629de272d..73e670c2485 100644 --- a/tests/swoole_curl/close_before_resume.phpt +++ b/tests/swoole_curl/close_before_resume.phpt @@ -54,7 +54,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { @@ -74,5 +74,5 @@ $pm->run(); Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_close(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/concurrent.phpt b/tests/swoole_curl/concurrent.phpt index ecfc719702c..74de7cda808 100644 --- a/tests/swoole_curl/concurrent.phpt +++ b/tests/swoole_curl/concurrent.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/event_exit.phpt b/tests/swoole_curl/event_exit.phpt new file mode 100644 index 00000000000..3fe0d988256 --- /dev/null +++ b/tests/swoole_curl/event_exit.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl: event exit +--SKIPIF-- + +--FILE-- + sprintf('https://rpm.nodesource.com/pub_%d.x/el/%d/x86_64/', $version, $os), + CURLOPT_RETURNTRANSFER => true + ]); + $response = curl_exec($ch); + curl_close($ch); + } + } +}); +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/exec_twice.phpt b/tests/swoole_curl/exec_twice.phpt index cfe1c04bcdd..012b1957506 100644 --- a/tests/swoole_curl/exec_twice.phpt +++ b/tests/swoole_curl/exec_twice.phpt @@ -52,7 +52,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { @@ -75,5 +75,5 @@ co 2 exec Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_exec(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/fatal_error_in_callback.phpt b/tests/swoole_curl/fatal_error_in_callback.phpt new file mode 100644 index 00000000000..2440856c054 --- /dev/null +++ b/tests/swoole_curl/fatal_error_in_callback.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_curl: error +--SKIPIF-- + +--FILE-- +getMessage(), E_USER_WARNING); + } + + }); + + curl_exec($ch); + echo "Exec\n"; + curl_close($ch); +}); +echo "Done\n"; +?> +--EXPECTF-- +Fatal error: test in %s on line %d + +Warning: curl_close(): Attempt to close cURL handle from a callback in %s on line %d diff --git a/tests/swoole_curl/ftp.phpt b/tests/swoole_curl/ftp.phpt new file mode 100644 index 00000000000..fa17a95e0cf --- /dev/null +++ b/tests/swoole_curl/ftp.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_curl: ftp +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle.phpt b/tests/swoole_curl/guzzle.phpt index 94ca0fc7d0d..cc25f8c30d5 100644 --- a/tests/swoole_curl/guzzle.phpt +++ b/tests/swoole_curl/guzzle.phpt @@ -19,6 +19,10 @@ use function Swoole\Coroutine\go; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); +register_shutdown_function(function (){ + phpt_show_usage(); +}); + const N = 4; run(function () { @@ -29,17 +33,17 @@ run(function () { $promises = [ 'baidu' => $client->getAsync('http://www.baidu.com/'), 'qq' => $client->getAsync('https://www.qq.com/'), - 'gov' => $client->getAsync('http://www.gov.cn/') + 'zhihu' => $client->getAsync('http://www.zhihu.com/') ]; $responses = Promise\Utils::unwrap($promises); Assert::contains($responses['baidu']->getBody(), '百度'); - Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); - Assert::contains($responses['gov']->getBody(), '中华人民共和国'); + Assert::contains($responses['qq']->getBody(), '腾讯'); + Assert::contains($responses['zhihu']->getBody(), '知乎'); $result['task_1'] = 'OK'; }); go(function () use ($barrier, &$result) { - $client = new Client(['base_uri' => 'http://httpbin.org/']); + $client = new Client(['base_uri' => 'https://httpbin.org/']); $n = N; $data = $promises = []; while ($n--) { diff --git a/tests/swoole_curl/guzzle/promise.phpt b/tests/swoole_curl/guzzle/promise.phpt index 5230f56e152..37a5bcfc5c2 100644 --- a/tests/swoole_curl/guzzle/promise.phpt +++ b/tests/swoole_curl/guzzle/promise.phpt @@ -18,17 +18,17 @@ use function Swoole\Coroutine\run; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { - $client = new Client(['base_uri' => 'http://httpbin.org']); + $client = new Client(['base_uri' => 'https://httpbin.org']); // Initiate each request but do not block $promises = [ 'a' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test1!']]), 'b' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test2!']]), - 'b' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]), + 'c' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]), ]; // Wait on all of the requests to complete. - $results = Promise\unwrap($promises); + $results = GuzzleHttp\Promise\Utils::unwrap($promises); // You can access each result using the key provided to the unwrap // function. diff --git a/tests/swoole_curl/guzzle/send_async.phpt b/tests/swoole_curl/guzzle/send_async.phpt index 495efee9e4b..33bf8b4b6b7 100644 --- a/tests/swoole_curl/guzzle/send_async.phpt +++ b/tests/swoole_curl/guzzle/send_async.phpt @@ -19,10 +19,10 @@ Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { $client = new Client(); - $response = $client->request('GET', 'https://api.github.com/repos/swoole/swoole-src'); + $response = $client->request('GET', 'https://httpbin.org'); echo $response->getStatusCode(), PHP_EOL; // 200 - echo $response->getHeaderLine('content-type'), PHP_EOL; // 'application/json; charset=utf8' + echo $response->getHeaderLine('content-type'), PHP_EOL; // Send an asynchronous request. $request = new Request('GET', 'http://httpbin.org'); @@ -36,6 +36,6 @@ run(function () { ?> --EXPECT-- 200 -application/json; charset=utf-8 +text/html; charset=utf-8 I completed! 200 Done diff --git a/tests/swoole_curl/https.phpt b/tests/swoole_curl/https.phpt index f71d2096302..6b6cb605122 100644 --- a/tests/swoole_curl/https.phpt +++ b/tests/swoole_curl/https.phpt @@ -22,7 +22,7 @@ run(function () { go(function() { $ch = curl_init(); $code = uniqid('swoole_'); - if (IS_IN_TRAVIS) { + if (IS_IN_CI) { $domain = 'www.google.com'; } else { $domain = 'www.baidu.com'; @@ -43,7 +43,7 @@ run(function () { echo "CURL Error:" . curl_error($ch); } Assert::notEmpty($output); - Assert::greaterThan(strlen($output), 10000); + Assert::greaterThan(strlen($output), 8192); curl_close($ch); }); } diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt index 4e734b7f89c..6ea973d2e47 100644 --- a/tests/swoole_curl/multi/bug4393.phpt +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -9,13 +9,11 @@ require __DIR__ . '/../../include/skipif.inc'; require __DIR__ . '/../../include/bootstrap.php'; require_once TESTS_LIB_PATH . '/vendor/autoload.php'; -use Swoole\Coroutine\Barrier; use Swoole\Runtime; use GuzzleHttp\Client; use GuzzleHttp\Promise; use function Swoole\Coroutine\run; -use function Swoole\Coroutine\go; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); @@ -23,25 +21,37 @@ run(function () { $guzzle = new Client(); $test = function () use ($guzzle) { - $promises = [ - 'qq' => $guzzle->getAsync('https://www.qq.com/'), - 'baidu' => $guzzle->getAsync('http://www.baidu.com/'), - ]; + if (IS_IN_CI) { + $promises = [ + 'qq' => $guzzle->getAsync('https://www.qq.com/'), + 'baidu' => $guzzle->getAsync('http://www.baidu.com/'), + ]; + } else { + $promises = [ + 'httpbin' => $guzzle->getAsync('https://www.httpbin.org/'), + 'nghttp2' => $guzzle->getAsync('https://nghttp2.org/'), + ]; + } $responses = []; foreach (Promise\Utils::settle($promises)->wait() as $k => $v) { $responses[$k] = $v['value']; } - Assert::contains($responses['baidu']->getBody(), '百度'); - Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); + if (IS_IN_CI) { + Assert::contains($responses['baidu']->getBody(), '百度'); + Assert::contains($responses['qq']->getBody(), '腾讯'); + } else { + Assert::contains($responses['httpbin']->getBody(), 'httpbin'); + Assert::contains($responses['nghttp2']->getBody(), 'nghttp2'); + } }; $n = 2; while ($n--) { $s = microtime(true); $test(); - Assert::lessThan(microtime(true) - $s, 1.5); + Assert::lessThan(microtime(true) - $s, 3.0); } echo 'Done' . PHP_EOL; diff --git a/tests/swoole_curl/multi/bug76675.phpt b/tests/swoole_curl/multi/bug76675.phpt index 748adce741c..2f9b8c4d9d0 100644 --- a/tests/swoole_curl/multi/bug76675.phpt +++ b/tests/swoole_curl/multi/bug76675.phpt @@ -32,7 +32,7 @@ $fn = function() { curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $callback); $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://http2.golang.org/serverpush'); + curl_setopt($ch, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL); curl_setopt($ch, CURLOPT_HTTP_VERSION, 3); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); @@ -41,9 +41,10 @@ $fn = function() { $active = null; do { $status = curl_multi_exec($mh, $active); - // echo "active=$active, status=$status\n"; + phpt_echo("active=$active, status=$status\n"); do { $info = curl_multi_info_read($mh); + phpt_echo($info); if (false !== $info && $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { @@ -53,6 +54,7 @@ $fn = function() { } } } while ($info); + curl_multi_select($mh); } while ($transfers); curl_multi_close($mh); }; diff --git a/tests/swoole_curl/multi/bug77535.phpt b/tests/swoole_curl/multi/bug77535.phpt index 69ad18ab599..5e91161faa2 100644 --- a/tests/swoole_curl/multi/bug77535.phpt +++ b/tests/swoole_curl/multi/bug77535.phpt @@ -35,7 +35,7 @@ class MyHttpClient curl_setopt($this->curl, CURLOPT_HEADER, false); curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, false); curl_setopt($this->curl, CURLOPT_FAILONERROR, false); - curl_setopt($this->curl, CURLOPT_URL, 'https://http2.golang.org/serverpush'); + curl_setopt($this->curl, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL); curl_setopt($this->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, function ($ch, $data) { return \strlen($data); diff --git a/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt index 8757b5689d4..114e2d3c4e0 100644 --- a/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt +++ b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt @@ -38,11 +38,13 @@ $cm->run(function ($host) { curl_multi_remove_handle($mh, $ch3); curl_multi_close($mh); }); +echo PHP_EOL; ?> ===DONE=== --EXPECT-- bool(true) -АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5===DONE=== +АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5 +===DONE=== --CLEAN-- --EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_close_basic001.phpt b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt index ea4dc86066a..95ce6225507 100644 --- a/tests/swoole_curl/multi/curl_multi_close_basic001.phpt +++ b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt @@ -15,10 +15,10 @@ use function Swoole\Coroutine\run; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { $cmh = curl_multi_init(); - curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); $multi_close_result = curl_multi_close($cmh); Assert::null($multi_close_result); - curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); curl_multi_close($cmh); }); ?> diff --git a/tests/swoole_curl/multi/curl_multi_close_reference.phpt b/tests/swoole_curl/multi/curl_multi_close_reference.phpt index df8ef147139..b948df0d957 100644 --- a/tests/swoole_curl/multi/curl_multi_close_reference.phpt +++ b/tests/swoole_curl/multi/curl_multi_close_reference.phpt @@ -22,8 +22,8 @@ run(function () { curl_multi_add_handle($mh, curl_init()); curl_multi_add_handle($mh, curl_init()); curl_multi_add_handle($mh, curl_init()); - echo "okey"; + echo "DONE\n"; }); ?> --EXPECT-- -okey +DONE diff --git a/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt b/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt index d1784403c15..fbdfc8a034b 100644 --- a/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt +++ b/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt @@ -14,28 +14,34 @@ use Swoole\Runtime; use function Swoole\Coroutine\run; -Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); -run(function () { - $mh = curl_multi_init(); - $errno = curl_multi_errno($mh); - echo $errno . PHP_EOL; - echo curl_multi_strerror($errno) . PHP_EOL; +$pm = ProcessManager::exec(function ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () { + $mh = curl_multi_init(); + $errno = curl_multi_errno($mh); + echo $errno . PHP_EOL; + echo curl_multi_strerror($errno) . PHP_EOL; - try { - curl_multi_setopt($mh, -1, -1); - } catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; - } + try { + curl_multi_setopt($mh, -1, -1); + } catch (ValueError $exception) { + echo $exception->getMessage() . "\n"; + } - $errno = curl_multi_errno($mh); - echo $errno . PHP_EOL; - echo curl_multi_strerror($errno) . PHP_EOL; + $errno = curl_multi_errno($mh); + echo $errno . PHP_EOL; + echo curl_multi_strerror($errno) . PHP_EOL; + }); }); -?> ---EXPECTF-- -0 -No error +$output = $pm->getChildOutput(); -Warning: curl_multi_setopt(): Invalid curl multi configuration option in %s on line %d -6 -Unknown option +Assert::contains($output, "0\nNo error"); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, "Warning: curl_multi_setopt(): Invalid curl multi configuration option"); +} else { + Assert::contains($output, "0\nNo error"); + Assert::contains($output, "curl_multi_setopt(): Argument #2 (\$option) is not a valid cURL multi option"); +} +Assert::contains($output, "6\nUnknown option"); +?> +--EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_init_basic.phpt b/tests/swoole_curl/multi/curl_multi_init_basic.phpt index 4b23d3b81c6..c043cb1f7b4 100644 --- a/tests/swoole_curl/multi/curl_multi_init_basic.phpt +++ b/tests/swoole_curl/multi/curl_multi_init_basic.phpt @@ -21,10 +21,10 @@ run(function () { //create the multiple cURL handle $mh = curl_multi_init(); - curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); curl_multi_close($mh); - curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); }); ?> --EXPECTF-- diff --git a/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt index 41838879689..202d7133846 100644 --- a/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt +++ b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt @@ -14,20 +14,27 @@ use Swoole\Runtime; use function Swoole\Coroutine\run; -Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); -run(function () { - $mh = curl_multi_init(); - var_dump(curl_multi_setopt($mh, CURLMOPT_PIPELINING, 0)); - - try { - curl_multi_setopt($mh, -1, 0); - } catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; - } - curl_multi_close($mh); +$pm = ProcessManager::exec(function ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () { + $mh = curl_multi_init(); + var_dump(curl_multi_setopt($mh, CURLMOPT_PIPELINING, 0)); + try { + curl_multi_setopt($mh, -1, 0); + } catch (ValueError $exception) { + echo $exception->getMessage() . "\n"; + } + curl_multi_close($mh); + }); }); -?> ---EXPECTF-- -bool(true) +$output = $pm->getChildOutput(); + +Assert::contains($output, 'bool(true)'); -Warning: curl_multi_setopt(): Invalid curl multi configuration option in %s on line %d +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Warning: curl_multi_setopt(): Invalid curl multi configuration option'); +} else { + Assert::contains($output, 'curl_multi_setopt(): Argument #2 ($option) is not a valid cURL multi option'); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/multi/no_hook.phpt b/tests/swoole_curl/multi/no_hook.phpt new file mode 100644 index 00000000000..4fc6e619305 --- /dev/null +++ b/tests/swoole_curl/multi/no_hook.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_curl/multi: no hook +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/select_cancel.phpt b/tests/swoole_curl/select_cancel.phpt index c1b68883187..9e1b8352a73 100644 --- a/tests/swoole_curl/select_cancel.phpt +++ b/tests/swoole_curl/select_cancel.phpt @@ -78,7 +78,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_curl/select_timeout.phpt b/tests/swoole_curl/select_timeout.phpt index 53136b1304f..48cb8379b2e 100644 --- a/tests/swoole_curl/select_timeout.phpt +++ b/tests/swoole_curl/select_timeout.phpt @@ -75,7 +75,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null', 'max_wait_time' => 1,]); $http->on("start", function ($server) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_curl/select_twice.phpt b/tests/swoole_curl/select_twice.phpt index 54d7daed6b9..35a4a9ecdc6 100644 --- a/tests/swoole_curl/select_twice.phpt +++ b/tests/swoole_curl/select_twice.phpt @@ -28,5 +28,5 @@ run(function () { Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_multi_select(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/setopt/filetime_1.phpt b/tests/swoole_curl/setopt/filetime_1.phpt index a2089fa32cc..8382fa8fe99 100644 --- a/tests/swoole_curl/setopt/filetime_1.phpt +++ b/tests/swoole_curl/setopt/filetime_1.phpt @@ -12,7 +12,7 @@ $cm->run(function ($host) { $ch = curl_init(); $options = array( - CURLOPT_URL => 'http://www.gov.cn/govweb/xhtml/libs/jQuery/jquery-e.min.js', + CURLOPT_URL => 'https://static.zhihu.com/heifetz/chunks/5946.4600cc0c1b3dcecac17c.js', CURLOPT_RETURNTRANSFER => 1, CURLOPT_FILETIME => true, CURLOPT_NOBODY => true, diff --git a/tests/swoole_curl/share/1.phpt b/tests/swoole_curl/share/1.phpt new file mode 100644 index 00000000000..0fc3d2ce023 --- /dev/null +++ b/tests/swoole_curl/share/1.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_curl/share: Basic curl_share test +--SKIPIF-- + +--FILE-- +run(function ($host) { + $sh = curl_share_init(); + + $ch1 = curl_init(); + curl_setopt($ch1, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata1.txt'); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch1, CURLOPT_SHARE, $sh); + + $ch2 = curl_init(); + curl_setopt($ch2, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata2.txt'); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch2, CURLOPT_SHARE, $sh); + + // Make sure nothing bad handles if the share handle is unset early. + unset($sh); + + var_dump(curl_exec($ch1)); + var_dump(curl_exec($ch2)); +}); + + +?> +--EXPECT-- +string(6) "CURL1 +" +string(6) "CURL2 +" diff --git a/tests/swoole_curl/share/5.phpt b/tests/swoole_curl/share/5.phpt new file mode 100644 index 00000000000..a86c616ba9b --- /dev/null +++ b/tests/swoole_curl/share/5.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_curl/share: Basic curl_share test +--SKIPIF-- + +--FILE-- +run(function ($host) { + $sh = curl_share_init(); + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + + $cookie_value = 'swoole'; + $cookie_name = 'test_cookie'; + + $url = "{$host}/get.php?test=cookie_set&cookie_name={$cookie_name}&cookie_value={$cookie_value}&cookie_expire=" . (time() + 3600); + + $ch1 = curl_init($url); + curl_setopt($ch1, CURLOPT_SHARE, $sh); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch1, CURLOPT_COOKIEFILE, ""); + $rs1 = curl_exec($ch1); + Assert::notEmpty($rs1); + Assert::eq(json_decode($rs1), true); + + $url = "{$host}/get.php?test=cookie_get"; + $ch2 = curl_init($url); + curl_setopt($ch2, CURLOPT_SHARE, $sh); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch2, CURLOPT_COOKIEFILE, ""); + $rs2 = curl_exec($ch2); + Assert::notEmpty($rs2); + Assert::eq(json_decode($rs2)->$cookie_name, $cookie_value); + curl_share_close($sh); + + curl_close($ch1); + curl_close($ch2); +}); + + +?> +--EXPECT-- diff --git a/tests/swoole_curl/sleep.phpt b/tests/swoole_curl/sleep.phpt index 50a2fc2ecff..b8b224de151 100644 --- a/tests/swoole_curl/sleep.phpt +++ b/tests/swoole_curl/sleep.phpt @@ -49,7 +49,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/ssl/version.phpt b/tests/swoole_curl/ssl/version.phpt index 5647a6a0bb3..fdeb18c6e31 100644 --- a/tests/swoole_curl/ssl/version.phpt +++ b/tests/swoole_curl/ssl/version.phpt @@ -16,10 +16,10 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_URL, "https://www.qq.com/"); + curl_setopt($ch, CURLOPT_URL, "https://www.baidu.com/"); $result = curl_exec($ch); Assert::assert($result); - Assert::contains($result, 'tencent'); + Assert::contains($result, '百度'); curl_close($ch); }, false); diff --git a/tests/swoole_curl/symfony-noco.phpt b/tests/swoole_curl/symfony-noco.phpt new file mode 100644 index 00000000000..b471bb78f69 --- /dev/null +++ b/tests/swoole_curl/symfony-noco.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_curl: symfony http client +--SKIPIF-- + +--FILE-- + 5]) +); +$uid = uniqid(); +$req = Psr17FactoryDiscovery::findRequestFactory() + ->createRequest('POST', 'http://www.httpbin.org/post') + ->withHeader('Content-Type', 'application/json') + ->withBody(Psr17FactoryDiscovery::findStreamFactory()->createStream(json_encode(['key' => $uid]))); + +$res = (new PluginClient($httpClient))->sendAsyncRequest($req)->wait(); + +$json = $res->getBody()->getContents(); +Assert::notEmpty($json); +$data_1 = json_decode($json); +$data_2 = json_decode($data_1->data); +Assert::eq($data_2->key, $uid); +echo 'Done' . PHP_EOL; + +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/undefined_behavior/0.phpt b/tests/swoole_curl/undefined_behavior/0.phpt new file mode 100644 index 00000000000..7dd1d0d6285 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/0.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl/undefined_behavior: 0 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "Warning: curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/1.phpt b/tests/swoole_curl/undefined_behavior/1.phpt new file mode 100644 index 00000000000..3307a567ea5 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/1.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/undefined_behavior: 1 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/2.phpt b/tests/swoole_curl/undefined_behavior/2.phpt new file mode 100644 index 00000000000..3ae066e829f --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/2.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl/undefined_behavior: 2 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_setopt(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/3.phpt b/tests/swoole_curl/undefined_behavior/3.phpt new file mode 100644 index 00000000000..fc6e70f2b45 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/3.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/undefined_behavior: 3 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/4.phpt b/tests/swoole_curl/undefined_behavior/4.phpt new file mode 100644 index 00000000000..7a8fd69c89f --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/4.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/undefined_behavior: 4 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/5.phpt b/tests/swoole_curl/undefined_behavior/5.phpt new file mode 100644 index 00000000000..395fbedd7f0 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/5.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/undefined_behavior: 5 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_multi_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Multi-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/6.phpt b/tests/swoole_curl/undefined_behavior/6.phpt new file mode 100644 index 00000000000..dae98af336b --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/6.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_curl/undefined_behavior: 6 +--SKIPIF-- + +--FILE-- +getChildOutput(); +$pm->expectExitCode(0); +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/7.phpt b/tests/swoole_curl/undefined_behavior/7.phpt new file mode 100644 index 00000000000..0fffbcfb777 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/7.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl/undefined_behavior: 7 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "Warning: curl_multi_close(): supplied resource is not a valid cURL Multi Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/8.phpt b/tests/swoole_curl/undefined_behavior/8.phpt new file mode 100644 index 00000000000..fa122e28928 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/8.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/undefined_behavior: 8 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: curl_exec(): The given handle is not initialized in coroutine in %s on line %d diff --git a/tests/swoole_curl/upload/1.phpt b/tests/swoole_curl/upload/1.phpt index 20cc1b20573..149238dce1d 100644 --- a/tests/swoole_curl/upload/1.phpt +++ b/tests/swoole_curl/upload/1.phpt @@ -32,7 +32,11 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); var_dump(curl_exec($ch)); - curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); + try { + curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); + } catch (throwable $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + } $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); var_dump(curl_exec($ch)); diff --git a/tests/swoole_event/add_after_server_start.phpt b/tests/swoole_event/add_after_server_start.phpt index 3dc021710ad..9fe59067db6 100644 --- a/tests/swoole_event/add_after_server_start.phpt +++ b/tests/swoole_event/add_after_server_start.phpt @@ -10,7 +10,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -const FILE = __DIR__.'/tmp_result.txt'; +const FILE = __DIR__ . '/tmp_result.txt'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { @@ -20,18 +20,18 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'log_file' => '/dev/null', )); $serv->on("start", function (Server $serv) use ($pm) { - $fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); - fwrite($fp, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + $fp = stream_socket_client("tcp://" . TEST_DOMAIN_2 . ":80", $errno, $errstr, 30); + fwrite($fp, "GET / HTTP/1.1\r\nHost: " . TEST_DOMAIN_2 . "\r\n\r\n"); - swoole_event_add($fp, function ($fp) use ($pm) { + Swoole\Event::add($fp, function ($fp) use ($pm) { $resp = fread($fp, 8192); - swoole_event_del($fp); + Swoole\Event::del($fp); fclose($fp); file_put_contents(FILE, $resp); $pm->wakeup(); diff --git a/tests/swoole_event/cycle.phpt b/tests/swoole_event/cycle.phpt new file mode 100644 index 00000000000..64851a1c9b8 --- /dev/null +++ b/tests/swoole_event/cycle.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_event: cycle +--SKIPIF-- + +--FILE-- + +--EXPECT-- +cycle [0] +timer [1] +defer [2] +cycle [1] +timer [2] +cycle [2] diff --git a/tests/swoole_event/defer.phpt b/tests/swoole_event/defer.phpt index 8cb131d85d0..519f48790c9 100644 --- a/tests/swoole_event/defer.phpt +++ b/tests/swoole_event/defer.phpt @@ -1,24 +1,24 @@ --TEST-- -swoole_event: swoole_event_defer +swoole_event: Swoole\Event::defer --SKIPIF-- --FILE-- --EXPECT-- defer [1] diff --git a/tests/swoole_event/defer_with_sleep.phpt b/tests/swoole_event/defer_with_sleep.phpt index c067934327b..a401b8f13ab 100644 --- a/tests/swoole_event/defer_with_sleep.phpt +++ b/tests/swoole_event/defer_with_sleep.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_defer and sleep +swoole_event: Swoole\Event::defer and sleep --SKIPIF-- --FILE-- @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { co::sleep(0.001); echo "timer [1]\n"; - swoole_event_defer(function () { + Swoole\Event::defer(function () { echo "defer [2]\n"; go(function () { co::sleep(0.001); @@ -16,10 +16,10 @@ go(function () { }); }); }); -swoole_event_defer(function () { +Swoole\Event::defer(function () { echo "defer [1]\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- defer [1] diff --git a/tests/swoole_event/defer_without_io.phpt b/tests/swoole_event/defer_without_io.phpt index 0b6844e46eb..95c8577f53e 100644 --- a/tests/swoole_event/defer_without_io.phpt +++ b/tests/swoole_event/defer_without_io.phpt @@ -1,16 +1,16 @@ --TEST-- -swoole_event: swoole_event_defer without io +swoole_event: Swoole\Event::defer without io --SKIPIF-- --FILE-- --EXPECT-- defer [1] diff --git a/tests/swoole_event/del.phpt b/tests/swoole_event/del.phpt new file mode 100644 index 00000000000..1693a2de461 --- /dev/null +++ b/tests/swoole_event/del.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_event: Swoole\Event::del +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_event/del_after_close.phpt b/tests/swoole_event/del_after_close.phpt index 9f87fe61dc1..c2e2e4ea76d 100644 --- a/tests/swoole_event/del_after_close.phpt +++ b/tests/swoole_event/del_after_close.phpt @@ -18,10 +18,10 @@ $cli->connect("www.qq.com", 80); $fd = $cli->sock; -Event::add($fd, function($fd) use($cli) { - $resp = fread($fp, 8192); - swoole_event_del($fp); - fclose($fp); +Event::add($fd, function ($fd) use ($cli) { + $resp = $cli->recv(8192); + Swoole\Event::del($fd); + $cli->close(); }); Event::write($fd, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); @@ -30,7 +30,7 @@ $cli->close(); if (Event::isset($fd)) { if (!Event::del($fd)) { - echo "Unable to release fd {$fd} from EventLoop\n"; + echo "Unable to release fd {$fd} from EventLoop\n"; } else { echo "FD {$fd} released from EventLoop\n"; } diff --git a/tests/swoole_event/dispatch.phpt b/tests/swoole_event/dispatch.phpt index db69f1e0e10..7b38407afd6 100644 --- a/tests/swoole_event/dispatch.phpt +++ b/tests/swoole_event/dispatch.phpt @@ -6,17 +6,17 @@ swoole_event: dispatch --EXPECT-- diff --git a/tests/swoole_event/function_alias.phpt b/tests/swoole_event/function_alias.phpt new file mode 100644 index 00000000000..483d2b32310 --- /dev/null +++ b/tests/swoole_event/function_alias.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_event: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/tests/swoole_event/isset.phpt b/tests/swoole_event/isset.phpt new file mode 100644 index 00000000000..ec66867ea4d --- /dev/null +++ b/tests/swoole_event/isset.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_event: Swoole\Event::isset +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_event/rshutdown.phpt b/tests/swoole_event/rshutdown.phpt index 138a952b169..c93adab3008 100644 --- a/tests/swoole_event/rshutdown.phpt +++ b/tests/swoole_event/rshutdown.phpt @@ -8,13 +8,13 @@ require __DIR__ . '/../include/bootstrap.php'; error_reporting(E_ALL & E_DEPRECATED); -swoole_event_add(STDIN, function ($fp) { +Swoole\Event::add(STDIN, function ($fp) { var_dump(fread($fp, 1024)); - swoole_event_del(STDIN); + Swoole\Event::del(STDIN); }); -swoole_timer_after(100, function () { - swoole_event_del(STDIN); +Swoole\Timer::after(100, function () { + Swoole\Event::del(STDIN); fclose(STDIN); }); diff --git a/tests/swoole_event/set.phpt b/tests/swoole_event/set.phpt new file mode 100644 index 00000000000..961720839ab --- /dev/null +++ b/tests/swoole_event/set.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_event: Swoole\Event::set +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Finish +write_callback: SUCCESS diff --git a/tests/swoole_event/simple.phpt b/tests/swoole_event/simple.phpt new file mode 100644 index 00000000000..d99a577b791 --- /dev/null +++ b/tests/swoole_event/simple.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_event: Swoole\Event::exit +--SKIPIF-- + +--FILE-- + + +--EXPECT-- +tick diff --git a/tests/swoole_event/sockets.phpt b/tests/swoole_event/sockets.phpt new file mode 100644 index 00000000000..d8eb3466bd2 --- /dev/null +++ b/tests/swoole_event/sockets.phpt @@ -0,0 +1,86 @@ +--TEST-- +swoole_event: add event after server start +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); + socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); + + function socket_onRead($socket) + { + static $i = 0; + $line = socket_read($socket, 8192); + if (!$line) { + exit("ERROR\n"); + } + Assert::eq($line, "Swoole: " . GREETING_MESSAGE); + if ($i > 10) { + echo "DONE\n"; + Event::del($socket); + socket_close($socket); + } else { + usleep(10000); + $i++; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } + } + + function socket_onWrite($socket) + { + socket_write($socket, GREETING_MESSAGE); + Event::set($socket, null, null, SWOOLE_EVENT_READ); + } + + function socket_onConnect($socket) + { + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "CONNECTED\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, GREETING_MESSAGE); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } + } + + Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); + socket_connect($socket, '127.0.0.1', $pm->getFreePort()); + Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'log_file' => '/dev/null', + )); + $serv->on("start", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + $serv->send($fd, "Swoole: $data"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +CONNECTED +DONE diff --git a/tests/swoole_event/swoole_event.phpt b/tests/swoole_event/swoole_event.phpt deleted file mode 100644 index a3714a1cc3e..00000000000 --- a/tests/swoole_event/swoole_event.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_event: swoole_event_exit ---SKIPIF-- - ---FILE-- - - ---EXPECT-- -tick diff --git a/tests/swoole_event/swoole_event_del.phpt b/tests/swoole_event/swoole_event_del.phpt deleted file mode 100644 index fb5a74a610c..00000000000 --- a/tests/swoole_event/swoole_event_del.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_event: swoole_event_del ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_event/swoole_event_isset.phpt b/tests/swoole_event/swoole_event_isset.phpt deleted file mode 100644 index 2b368b5b25c..00000000000 --- a/tests/swoole_event/swoole_event_isset.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_event: swoole_event_isset ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_event/swoole_event_set.phpt b/tests/swoole_event/swoole_event_set.phpt deleted file mode 100644 index a76c72bd0a1..00000000000 --- a/tests/swoole_event/swoole_event_set.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_event: swoole_event_set ---SKIPIF-- - ---FILE-- - ---EXPECT-- -Finish -read_callback:SUCCESS diff --git a/tests/swoole_event/swoole_event_wait.phpt b/tests/swoole_event/swoole_event_wait.phpt deleted file mode 100644 index 9f0fbe6a2c8..00000000000 --- a/tests/swoole_event/swoole_event_wait.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_event: swoole_event_wait (auto) ---SKIPIF-- - ---FILE-- - ---EXPECT-- -register 1 -register 2 -register 3 -register 4 -register 5 -register 6 -register 7 -register 8 diff --git a/tests/swoole_event/swoole_event_write.phpt b/tests/swoole_event/swoole_event_write.phpt deleted file mode 100644 index 740ceba03fa..00000000000 --- a/tests/swoole_event/swoole_event_write.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_event: swoole_event_write ---SKIPIF-- - ---FILE-- - ---EXPECT-- -Finish -SUCCESS diff --git a/tests/swoole_event/wait.phpt b/tests/swoole_event/wait.phpt new file mode 100644 index 00000000000..703640c1f25 --- /dev/null +++ b/tests/swoole_event/wait.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_event: wait (auto) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +register 1 +register 2 +register 3 +register 4 +register 5 +register 6 +register 7 +register 8 diff --git a/tests/swoole_event/write.phpt b/tests/swoole_event/write.phpt new file mode 100644 index 00000000000..6b4d3cf8739 --- /dev/null +++ b/tests/swoole_event/write.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_event: write() +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Finish +SUCCESS diff --git a/tests/swoole_feature/cross_close/client.phpt b/tests/swoole_feature/cross_close/client.phpt index 75cdbe9f960..f480f9fb543 100644 --- a/tests/swoole_feature/cross_close/client.phpt +++ b/tests/swoole_feature/cross_close/client.phpt @@ -19,10 +19,8 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); echo "DONE\n"; }); - Assert::assert(!($ret = @$cli->recv(-1))); - if ($ret === false) { - Assert::same($cli->errCode, SOCKET_ECONNRESET); - } + Assert::false(@$cli->recv(-1)); + Assert::same($cli->errCode, SOCKET_ECANCELED); echo "CLOSED\n"; Assert::assert(!$cli->connected); }); diff --git a/tests/swoole_feature/cross_close/full_duplex.phpt b/tests/swoole_feature/cross_close/full_duplex.phpt index 882ee0a4798..662630d11c7 100644 --- a/tests/swoole_feature/cross_close/full_duplex.phpt +++ b/tests/swoole_feature/cross_close/full_duplex.phpt @@ -22,14 +22,16 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($cli) { echo "SEND\n"; $size = 16 * 1024 * 1024; - Assert::assert($cli->send(str_repeat('S', $size)) < $size); + Assert::lessThan($cli->send(str_repeat('S', $size)), $size); Assert::assert(!$cli->connected); + Assert::eq($cli->errCode, SOCKET_ECANCELED); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; - Assert::assert(!$cli->recv(-1)); + Assert::false($cli->recv(-1)); Assert::assert(!$cli->connected); + Assert::eq($cli->errCode, SOCKET_ECANCELED); echo "RECV CLOSED\n"; }); }); diff --git a/tests/swoole_feature/cross_close/http.phpt b/tests/swoole_feature/cross_close/http.phpt index 09307ab0fca..a7d1573ea9d 100644 --- a/tests/swoole_feature/cross_close/http.phpt +++ b/tests/swoole_feature/cross_close/http.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { Assert::assert(!$http->get('/')); echo "CLOSED\n"; Assert::same($http->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); - Assert::same($http->errCode, SOCKET_ECONNRESET); + Assert::same($http->errCode, SOCKET_ECANCELED); Assert::assert(empty($http->body)); }); Swoole\Event::wait(); diff --git a/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt b/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt index cc2905e9727..fe09d746958 100644 --- a/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt +++ b/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt @@ -5,38 +5,47 @@ swoole_feature/cross_close: full duplex (php stream) --FILE-- parentFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $cli = stream_socket_client("tcp://127.0.0.1:{$pm->getFreePort()}", $errno, $errstr, 1); Assert::true(!$errno); go(function () use ($pm, $cli) { Co::sleep(0.001); echo "CLOSE\n"; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(fclose($cli)); // double close - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); $pm->kill(); echo "DONE\n"; }); go(function () use ($cli) { echo "SEND\n"; $size = 64 * 1024 * 1024; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size); - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(empty(fread($cli, 8192))); - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); echo "RECV CLOSED\n"; }); }); }; $pm->childFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); Assert::true($server->bind('127.0.0.1', $pm->getFreePort())); Assert::true($server->listen()); diff --git a/tests/swoole_feature/cross_close/redis.phpt b/tests/swoole_feature/cross_close/redis.phpt index 139f4257bf9..b48d371cbb4 100644 --- a/tests/swoole_feature/cross_close/redis.phpt +++ b/tests/swoole_feature/cross_close/redis.phpt @@ -8,7 +8,8 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { - $redis = new Co\Redis; + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + $redis = new \redis; go(function () use ($pm, $redis) { $redis->connect('127.0.0.1', $pm->getFreePort()); go(function () use ($pm, $redis) { @@ -20,14 +21,13 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; $pm->kill(); }); - $ret = $redis->get($pm->getRandomData()); - echo "CLOSED\n"; - Assert::assert(!$ret); - Assert::assert(!$redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECONNRESET); + try { + $ret = $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + $ret = false; + echo "CLOSED\n"; } + Assert::assert(!$ret); }); }); }; @@ -37,6 +37,7 @@ $pm->childFunc = function () use ($pm) { Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); Assert::assert($server->listen()); go(function () use ($pm, $server) { + $pm->wakeup(); if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { switch_process(); $data = $conn->recv(); @@ -49,7 +50,6 @@ $pm->childFunc = function () use ($pm) { } $server->close(); }); - $pm->wakeup(); }); }; $pm->childFirst(); diff --git a/tests/swoole_feature/cross_close/redis_by_server.phpt b/tests/swoole_feature/cross_close/redis_by_server.phpt index ebe728b7d79..a4436cfe99e 100644 --- a/tests/swoole_feature/cross_close/redis_by_server.phpt +++ b/tests/swoole_feature/cross_close/redis_by_server.phpt @@ -8,14 +8,16 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () use ($pm) { - $redis = new Co\Redis; + $redis = new \redis; Assert::assert($redis->connect('127.0.0.1', $pm->getFreePort())); echo "GET\n"; - Assert::assert(!$redis->get($pm->getRandomData())); - echo "CLOSED\n"; - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - Assert::same($redis->errCode, SOCKET_ECONNRESET); + try { + $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + echo "CLOSED\n"; + } $pm->kill(); echo "DONE\n"; }); diff --git a/tests/swoole_feature/cross_close/stream.phpt b/tests/swoole_feature/cross_close/stream.phpt index a33730d529b..e815b9d7605 100644 --- a/tests/swoole_feature/cross_close/stream.phpt +++ b/tests/swoole_feature/cross_close/stream.phpt @@ -5,29 +5,40 @@ swoole_feature/cross_close: stream --FILE-- ---EXPECTF-- -READ -CLOSE -CLOSED -Warning: fclose(): supplied resource is not a valid stream resource in %s/tests/swoole_feature/cross_close/stream.php on line 18 -DONE +$output = $pm->getChildOutput(); +Assert::contains($output, "READ\nCLOSE\nCLOSED\n"); + +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, "fclose(): supplied resource is not a valid stream resource"); +} else { + Assert::contains($output, "Fatal error: Uncaught TypeError: fclose(): supplied resource is not a valid stream resource"); +} + +?> +--EXPECT-- diff --git a/tests/swoole_feature/full_duplex/client.phpt b/tests/swoole_feature/full_duplex/client.phpt index 6f78ce2569d..7ac1b7f48a4 100644 --- a/tests/swoole_feature/full_duplex/client.phpt +++ b/tests/swoole_feature/full_duplex/client.phpt @@ -83,7 +83,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_feature/full_duplex/socket.phpt b/tests/swoole_feature/full_duplex/socket.phpt index 962e90ec6cd..087d00d4bdb 100644 --- a/tests/swoole_feature/full_duplex/socket.phpt +++ b/tests/swoole_feature/full_duplex/socket.phpt @@ -77,7 +77,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_feature/full_duplex/socket_ssl.phpt b/tests/swoole_feature/full_duplex/socket_ssl.phpt index e857104e468..f742696da46 100644 --- a/tests/swoole_feature/full_duplex/socket_ssl.phpt +++ b/tests/swoole_feature/full_duplex/socket_ssl.phpt @@ -80,7 +80,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_function/substr_unserialize.phpt b/tests/swoole_function/substr_unserialize.phpt index 7ee9d98483b..efdd0324776 100644 --- a/tests/swoole_function/substr_unserialize.phpt +++ b/tests/swoole_function/substr_unserialize.phpt @@ -12,11 +12,11 @@ $a['int'] = rand(1, 999999); $a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']; $val = serialize($a); -$str = pack('N', strlen($val)).$val."\r\n"; +$str = pack('N', strlen($val)) . $val . "\r\n"; $l = strlen($str) - 6; Assert::eq(swoole_substr_unserialize($str, 4, $l), $a); -Assert::eq(swoole_substr_unserialize($str, 4), $a); +Assert::eq(@swoole_substr_unserialize($str, 4), $a); Assert::eq(@swoole_substr_unserialize($str, 0), false); Assert::eq(@swoole_substr_unserialize($str, 6), false); Assert::eq(@swoole_substr_unserialize($str, 4, $l - 4), false); diff --git a/tests/swoole_function/swoole_error_log.phpt b/tests/swoole_function/swoole_error_log.phpt new file mode 100644 index 00000000000..c47ce1b0921 --- /dev/null +++ b/tests/swoole_function/swoole_error_log.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_function: swoole_error_log +--SKIPIF-- + +--FILE-- + LOG_FILE]); +swoole_error_log(SWOOLE_LOG_NOTICE, "hello 1"); +swoole_error_log_ex(SWOOLE_LOG_NOTICE, ERROR_1, "hello 2"); + +swoole_ignore_error(ERROR_2); +swoole_error_log_ex(SWOOLE_LOG_NOTICE, ERROR_2, "hello 3"); + +$content = file_get_contents(LOG_FILE); +Assert::contains($content, 'hello 1'); +Assert::contains($content, 'hello 2'); +Assert::contains($content, '(ERRNO ' . ERROR_1 . ')'); +Assert::notContains($content, 'hello 3'); +unlink(LOG_FILE); +?> +--EXPECT-- diff --git a/tests/swoole_global/channel_construct_check.phpt b/tests/swoole_global/channel_construct_check.phpt index c33fd6461d2..a796002c3a9 100644 --- a/tests/swoole_global/channel_construct_check.phpt +++ b/tests/swoole_global/channel_construct_check.phpt @@ -5,16 +5,18 @@ swoole_global: socket construct check --FILE-- push('123'); + +$pm = ProcessManager::exec(function () { + go(function () { + $chan = new class () extends Co\Channel { + public function __construct($size = 1) + { + // parent::__construct($size); // without parent call + } + }; + $chan->push('123'); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> ---EXPECTF-- -Fatal error: Swoole\Coroutine\Channel::push(): you must call Channel constructor first in %s on line %d +--EXPECT-- diff --git a/tests/swoole_global/closed_stdout.phpt b/tests/swoole_global/closed_stdout.phpt new file mode 100644 index 00000000000..10fb1bb245e --- /dev/null +++ b/tests/swoole_global/closed_stdout.phpt @@ -0,0 +1,9 @@ +--TEST-- +swoole_global: handle closed STDOUT/STDERR without exception +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_global/function_alias.phpt b/tests/swoole_global/function_alias.phpt new file mode 100644 index 00000000000..c3eae1255a1 --- /dev/null +++ b/tests/swoole_global/function_alias.phpt @@ -0,0 +1,13 @@ +--TEST-- +swoole_global: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/tests/swoole_global/serialize_deny.phpt b/tests/swoole_global/serialize_deny.phpt index 82a24e8ec44..6d9154d7f38 100644 --- a/tests/swoole_global/serialize_deny.phpt +++ b/tests/swoole_global/serialize_deny.phpt @@ -34,22 +34,6 @@ go(function () { } catch (\Exception $exception) { Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); } - try { - $hcc = new \Swoole\Coroutine\Mysql(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - if (HAS_ASYNC_REDIS) { - try { - $hcc = new \Swoole\Coroutine\Redis(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - } try { $hcc = new \Swoole\Table(1); serialize($hcc); diff --git a/tests/swoole_global/socket_construct_check.phpt b/tests/swoole_global/socket_construct_check.phpt index b9c97b54c41..1a18346ffed 100644 --- a/tests/swoole_global/socket_construct_check.phpt +++ b/tests/swoole_global/socket_construct_check.phpt @@ -5,16 +5,17 @@ swoole_global: socket construct check --FILE-- connect('127.0.0.1', 12345); +$pm = ProcessManager::exec(function () { + go(function () { + $socket = new class (1, 2, 3) extends Co\Socket { + public function __construct($domain, $type, $protocol) + { + // parent::__construct($domain, $type, $protocol); // without parent call + } + }; + $socket->connect('127.0.0.1', 12345); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> --EXPECTF-- -Fatal error: Swoole\Coroutine\Socket::connect(): you must call Socket constructor first in %s on line %d diff --git a/tests/swoole_global/too_many_objects.phpt b/tests/swoole_global/too_many_objects.phpt index 0d747a24f64..13ca312dede 100644 --- a/tests/swoole_global/too_many_objects.phpt +++ b/tests/swoole_global/too_many_objects.phpt @@ -9,7 +9,7 @@ $object_store = []; for ($n = 65536; $n--;) { $object_store[] = new stdClass(); } -$server = new swoole_websocket_server('127.0.0.1', get_one_free_port(), SWOOLE_BASE); +$server = new Swoole\WebSocket\Server('127.0.0.1', get_one_free_port(), SWOOLE_BASE); $tcp_server = $server->listen('127.0.0.1', get_one_free_port(), SWOOLE_TCP); echo "DONE\n"; ?> diff --git a/tests/swoole_http2_client_coro/bug_5127.phpt b/tests/swoole_http2_client_coro/bug_5127.phpt new file mode 100644 index 00000000000..6636433d60e --- /dev/null +++ b/tests/swoole_http2_client_coro/bug_5127.phpt @@ -0,0 +1,14 @@ +--TEST-- +swoole_http2_client_coro: Github #5127 When use swoole in php 8.2,Swoole\Http2\Request may throw ErrorException:Creation of dynamic property $usePipelineRead +--SKIPIF-- + +--FILE-- +usePipelineRead = true; +echo 'DONE'; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http2_client_coro/connect_twice.phpt b/tests/swoole_http2_client_coro/connect_twice.phpt index 798ca5f724b..88ff09e8a2f 100644 --- a/tests/swoole_http2_client_coro/connect_twice.phpt +++ b/tests/swoole_http2_client_coro/connect_twice.phpt @@ -6,29 +6,33 @@ swoole_http2_client_coro: connect twice connect(); - $req = new \Swoole\Http2\Request(); + $req = new Request(); + $uuid = uniqid(); $req->method = 'GET'; - $req->path = '/io?io=' . str_repeat('xxx', 1000); + $req->path = '/base64/' . base64_encode($uuid); $client->send($req); $chan->push(true); $resp = $client->recv(); Assert::eq($resp->statusCode, 200); - Assert::contains($resp->data, '知乎'); + Assert::eq($resp->data, $uuid); $chan->pop(); }); go(function () use ($client, $chan) { Assert::eq($client->connect(), false); - $req = new \Swoole\Http2\Request(); + $uuid = uniqid(); + $req = new Request(); $req->method = 'GET'; - $req->path = '/io?io=xxx'; + $req->path = '/base64/' . base64_encode($uuid); $client->send($req); $chan->push(true); Assert::eq($client->recv(), false); diff --git a/tests/swoole_http2_client_coro/host.phpt b/tests/swoole_http2_client_coro/host.phpt index b20c86922e5..532b58877a2 100644 --- a/tests/swoole_http2_client_coro/host.phpt +++ b/tests/swoole_http2_client_coro/host.phpt @@ -42,7 +42,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ // 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -51,7 +51,7 @@ $pm->childFunc = function () use ($pm) { 'ssl_key_file' => SSL_FILE_DIR . '/server.key' ]); $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::same($request->header['host'], "{$http->host}:{$http->port}"); unset($request->header['host']); foreach ($request->header as $name => $value) { diff --git a/tests/swoole_http2_client_coro/huge_headers.phpt b/tests/swoole_http2_client_coro/huge_headers.phpt index 54a7d0510a9..b7c811c499c 100644 --- a/tests/swoole_http2_client_coro/huge_headers.phpt +++ b/tests/swoole_http2_client_coro/huge_headers.phpt @@ -45,10 +45,10 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::assert(!$cli->send($request)); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ // 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { 'ssl_key_file' => SSL_FILE_DIR . '/server.key' ]); $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { foreach ($request->header as $name => $value) { $response->header($name, $value); } diff --git a/tests/swoole_http2_client_coro/multi.phpt b/tests/swoole_http2_client_coro/multi.phpt index da2f6e29200..7d823977d7f 100644 --- a/tests/swoole_http2_client_coro/multi.phpt +++ b/tests/swoole_http2_client_coro/multi.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set([ 'timeout' => -1, @@ -18,14 +18,14 @@ go(function () { Assert::true($cli->connected); $req = new Swoole\Http2\Request; - $req->path = '/terms/privacy'; + $req->path = '/'; $req->headers = [ 'Host' => $domain, "User-Agent" => 'Chrome/49.0.2587.3', 'Accept' => 'text/html,application/xhtml+xml,application/xml', 'Accept-encoding' => 'gzip', ]; - /**@var $response swoole_http2_response */ + /**@var $response swoole\http2\response */ $i = 4; while ($i--) { Assert::assert($cli->send($req)); @@ -34,11 +34,12 @@ go(function () { $responses_headers_count_map = []; $i = 0; while ($cli->connected) { - $response = $cli->recv(0.1); // it's for the test, you should make timeout bigger + // it's for the test, you should make timeout bigger + $response = $cli->recv(0.1); if ($response) { echo "$response->statusCode\n"; $responses_headers_count_map[] = count($response->headers); - Assert::contains($response->data, 'Cookie'); + Assert::contains($response->data, 'nghttp2.org'); $stream_map[] = $response->streamId; if (++$i === 4) { break; @@ -49,7 +50,7 @@ go(function () { Assert::lessThanEq(count(array_unique($responses_headers_count_map)), 2); Assert::assert($responses_headers_count_map[0] > 10); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- 200 diff --git a/tests/swoole_http2_client_coro/no-gzip.phpt b/tests/swoole_http2_client_coro/no-gzip.phpt index ec7a583f22e..ce5e93ecdc6 100644 --- a/tests/swoole_http2_client_coro/no-gzip.phpt +++ b/tests/swoole_http2_client_coro/no-gzip.phpt @@ -3,7 +3,7 @@ swoole_http2_client_coro: http2 without gzip and recv big data (window-update) --SKIPIF-- --FILE-- diff --git a/tests/swoole_http2_client_coro/post.phpt b/tests/swoole_http2_client_coro/post.phpt index 19dc7cc61b4..d789b3d4afd 100644 --- a/tests/swoole_http2_client_coro/post.phpt +++ b/tests/swoole_http2_client_coro/post.phpt @@ -29,7 +29,7 @@ go(function () { $req->data = '{"type":"up"}'; $cli->send($req); $response = $cli->recv(); - Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002], true)); + Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002, 100], true)); }); ?> --EXPECT-- diff --git a/tests/swoole_http2_server/big_data.phpt b/tests/swoole_http2_server/big_data.phpt index 2669d92e965..767d945976b 100644 --- a/tests/swoole_http2_server/big_data.phpt +++ b/tests/swoole_http2_server/big_data.phpt @@ -35,17 +35,17 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', 'open_http2_protocol' => true, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm) { $http->on("WorkerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end($request->rawcontent()); }); $http->start(); diff --git a/tests/swoole_http2_server/compression.phpt b/tests/swoole_http2_server/compression.phpt index b89b6be9aac..e4d9cdadbab 100644 --- a/tests/swoole_http2_server/compression.phpt +++ b/tests/swoole_http2_server/compression.phpt @@ -33,10 +33,10 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -44,7 +44,7 @@ $pm->childFunc = function () use ($pm) { 'http_compression' => true, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm) { global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http2_server/compression_types.phpt b/tests/swoole_http2_server/compression_types.phpt new file mode 100644 index 00000000000..6745e3ef29c --- /dev/null +++ b/tests/swoole_http2_server/compression_types.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http2_server: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http2_compression_types_test($pm); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + 'open_http2_protocol' => true, + ]); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http2_server/getMethod.phpt b/tests/swoole_http2_server/getMethod.phpt new file mode 100644 index 00000000000..7a20f89d2eb --- /dev/null +++ b/tests/swoole_http2_server/getMethod.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http2_server: getMethod +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + $req = new Swoole\Http2\Request(); + $req->method = 'POST'; + $req->path = '/api'; + $req->headers = [ + 'user-agent' => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip' + ]; + $req->data = '{"type":"up"}'; + $cli->send($req); + $response = $cli->recv(); + $json = json_decode($response->data); + Assert::same($json->request_method, 'POST'); + Assert::same($json->getMethod, 'POST'); + $pm->kill(); + }); + Swoole\Event::wait(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('::', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP6); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $request_method = $request->server['request_method']; + $getMethod = $request->getMethod(); + $response->end(json_encode(compact('request_method', 'getMethod'), JSON_PRETTY_PRINT) . "\n"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http2_server/goaway.phpt b/tests/swoole_http2_server/goaway.phpt index 09893bec0c5..8bd19ec755a 100644 --- a/tests/swoole_http2_server/goaway.phpt +++ b/tests/swoole_http2_server/goaway.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->goaway(SWOOLE_HTTP2_ERROR_NO_ERROR, 'NO_ERROR'); $response->end($request->rawcontent()); }); diff --git a/tests/swoole_http2_server/http2_headers.phpt b/tests/swoole_http2_server/http2_headers.phpt index 0f95b638ef8..4f7ff14e5c5 100644 --- a/tests/swoole_http2_server/http2_headers.phpt +++ b/tests/swoole_http2_server/http2_headers.phpt @@ -8,11 +8,16 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { $output = `curl --http2-prior-knowledge --silent -I http://127.0.0.1:{$pm->getFreePort()}`; - echo $output; + Assert::contains($output, 'HTTP/2 200'); + Assert::contains($output, 'test-value: a'); + Assert::contains($output, 'test-value: d5678'); + Assert::contains($output, 'test-value: e'); + Assert::contains($output, 'test-value: 5678'); + Assert::contains($output, 'test-value: 3.1415926'); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -21,7 +26,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('test-value', [ "a\r\n", 'd5678', @@ -37,14 +42,4 @@ $pm->childFunc = function () use ($pm) { $pm->childFirst(); $pm->run(); ?> ---EXPECTF-- -HTTP/2 200 -test-value: a -test-value: d5678 -test-value: e -test-value: 5678 -test-value: 3.1415926 -server: swoole-http-server -date: %s -content-type: text/html -content-length: 22 +--EXPECT-- diff --git a/tests/swoole_http2_server/issue_4365.phpt b/tests/swoole_http2_server/issue_4365.phpt index ab304338d1c..34f2581283c 100644 --- a/tests/swoole_http2_server/issue_4365.phpt +++ b/tests/swoole_http2_server/issue_4365.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'open_http2_protocol' => true, 'enable_reuse_port' => true, diff --git a/tests/swoole_http2_server/max_concurrency.phpt b/tests/swoole_http2_server/max_concurrency.phpt new file mode 100644 index 00000000000..388bd115c18 --- /dev/null +++ b/tests/swoole_http2_server/max_concurrency.phpt @@ -0,0 +1,95 @@ +--TEST-- +swoole_http2_server: max_concurrency +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + + $n = SERVER_MAX_CONCURRENCY; + // 200, low concurrency + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + System::sleep(0.005); + + // 503, high concurrency + $n = SERVER_MAX_CONCURRENCY; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 503); + }); + } + + // wait + Coroutine::join($cid_list); + + // low concurrency + $n = SERVER_MAX_CONCURRENCY; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + Coroutine::join($cid_list); + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'log_file' => '/dev/null', + 'max_concurrency' => SERVER_MAX_CONCURRENCY, + 'open_http2_protocol' => true, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http2_server/nghttp2_big_data.phpt b/tests/swoole_http2_server/nghttp2_big_data.phpt index 232dddb2cb3..5563c9ea0ae 100644 --- a/tests/swoole_http2_server/nghttp2_big_data.phpt +++ b/tests/swoole_http2_server/nghttp2_big_data.phpt @@ -3,23 +3,21 @@ swoole_http2_server: nghttp2 big data with ssl --SKIPIF-- &1`, 'nghttp2') === false) { - skip('no nghttp'); -} +skip_if_no_nghttp(); ?> --FILE-- parentFunc = function ($pid) use ($pm) { - $file = __DIR__ . '/../../benchmark/post.big.data'; + $file = TEST_IMAGE; if (Assert::assert(!empty($res = `nghttp -d {$file} https://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null`))) { Assert::same(md5($res), md5_file($file)); } $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,8 +28,8 @@ $pm->childFunc = function () use ($pm) { $http->on("WorkerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { - $response->end($request->rawcontent()); + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end($request->getContent()); }); $http->start(); }; diff --git a/tests/swoole_http2_server/no_compression.phpt b/tests/swoole_http2_server/no_compression.phpt index d1400c0d423..4e5b8c9d17b 100644 --- a/tests/swoole_http2_server/no_compression.phpt +++ b/tests/swoole_http2_server/no_compression.phpt @@ -32,17 +32,17 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'log_file' => '/dev/null', 'open_http2_protocol' => true, 'http_compression' => false, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http2_server/ping.phpt b/tests/swoole_http2_server/ping.phpt index 95e519b2e0d..0f9775dc654 100644 --- a/tests/swoole_http2_server/ping.phpt +++ b/tests/swoole_http2_server/ping.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Swoole\Timer::tick(100, function (int $id) use ($request, $response) { Assert::true($response->ping()); if (@++$GLOBALS['i'] === 5) { diff --git a/tests/swoole_http2_server/sendfile_content_type.phpt b/tests/swoole_http2_server/sendfile_content_type.phpt new file mode 100644 index 00000000000..524729fe074 --- /dev/null +++ b/tests/swoole_http2_server/sendfile_content_type.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http2_server: sendfile [content-type] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(), false); + $cli->set(['timeout' => -1]); + Assert::true($cli->connect()); + Assert::assert($cli->send(new Swoole\Http2\Request)); + $response = $cli->recv(); + Assert::notEmpty($response); + Assert::same(md5_file(TEST_IMAGE), md5($response->data)); + Assert::same($response->statusCode, 200); + Assert::same($response->headers['content-type'], 'image/jpeg'); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + if (!Assert::true($response->sendfile(TEST_IMAGE))) { + echo swoole_strerror(swoole_last_error()) . PHP_EOL; + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http2_server/sendfile_set_content_type.phpt b/tests/swoole_http2_server/sendfile_set_content_type.phpt new file mode 100644 index 00000000000..51828f9ee42 --- /dev/null +++ b/tests/swoole_http2_server/sendfile_set_content_type.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http2_server: sendfile [set content-type] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(), false); + $cli->set(['timeout' => -1]); + Assert::true($cli->connect()); + Assert::assert($cli->send(new Swoole\Http2\Request)); + $response = $cli->recv(); + Assert::notEmpty($response); + Assert::same($response->statusCode, 200); + Assert::same($response->headers['content-type'], 'application/javascript'); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $response->setHeader('Content-Type', 'application/javascript'); + if (!Assert::true($response->sendfile(__FILE__))) { + echo swoole_strerror(swoole_last_error()) . PHP_EOL; + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http2_server/streaming.phpt b/tests/swoole_http2_server/streaming.phpt new file mode 100644 index 00000000000..0475e3de4c4 --- /dev/null +++ b/tests/swoole_http2_server/streaming.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http2_server: streaming +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $proc = ChildProcess::exec("nghttp http://127.0.0.1:{$pm->getFreePort()}/"); + $out = ''; + while($line = $proc->read()) { + $out .= $line; + if (str_contains($line, 'hello world, end')) { + break; + } + } + echo $out; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + ]); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n\n"); + Co\System::sleep(0.1); + } + $response->end("hello world, end\n"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +hello world, #4 +hello world, #3 +hello world, #2 +hello world, #1 +hello world, #0 +hello world, end diff --git a/tests/swoole_http2_server/streaming_2.phpt b/tests/swoole_http2_server/streaming_2.phpt new file mode 100644 index 00000000000..66e6e568623 --- /dev/null +++ b/tests/swoole_http2_server/streaming_2.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http2_server: streaming 2 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $proc = ChildProcess::exec("nghttp http://127.0.0.1:{$pm->getFreePort()}/"); + $out = ''; + while($line = $proc->read()) { + $out .= $line; + if (str_contains($line, 'hello world, #0')) { + break; + } + } + echo $out; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + ]); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n\n"); + Co\System::sleep(0.1); + } + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +hello world, #4 +hello world, #3 +hello world, #2 +hello world, #1 +hello world, #0 diff --git a/tests/swoole_http2_server/worker_max_concurrency.phpt b/tests/swoole_http2_server/worker_max_concurrency.phpt new file mode 100644 index 00000000000..6384dcf0399 --- /dev/null +++ b/tests/swoole_http2_server/worker_max_concurrency.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_http2_server: worker_max_concurrency +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $n = SERVER_WORKER_MAX_CONCURRENCY * 3; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + // wait + Coroutine::join($cid_list); + + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + $req = new Swoole\Http2\Request; + $req->path = '/stats'; + Assert::greaterThan($streamId = $cli->send($req), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + $json = json_decode($response->data); + Assert::true(isset($json->coroutine_peek_num)); + Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY); + + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { +// $mode = SWOOLE_BASE; + $mode = SERVER_MODE_RANDOM; + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true, + 'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $http) { + if ($request->server['request_uri'] == '/stats') { + $response->end(json_encode($http->stats())); + return; + } + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/204.phpt b/tests/swoole_http_client_coro/204.phpt index 6625fad097b..253da87b689 100644 --- a/tests/swoole_http_client_coro/204.phpt +++ b/tests/swoole_http_client_coro/204.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: http 204 no content --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/addData.phpt b/tests/swoole_http_client_coro/addData.phpt index 3b3a8bb3c66..07b0472dd9c 100644 --- a/tests/swoole_http_client_coro/addData.phpt +++ b/tests/swoole_http_client_coro/addData.phpt @@ -19,8 +19,8 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event::wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_http_client_coro/alias.phpt b/tests/swoole_http_client_coro/alias.phpt index b24b699a440..7cb3af28df4 100644 --- a/tests/swoole_http_client_coro/alias.phpt +++ b/tests/swoole_http_client_coro/alias.phpt @@ -22,7 +22,7 @@ go(function () { Assert::assert(count($cli->getCookies()) > 2); echo "DONE\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_http_client_coro/another_coroutine.phpt b/tests/swoole_http_client_coro/another_coroutine.phpt index e8319147f1c..afa1543c22c 100644 --- a/tests/swoole_http_client_coro/another_coroutine.phpt +++ b/tests/swoole_http_client_coro/another_coroutine.phpt @@ -10,7 +10,7 @@ skip_unsupported(); require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function (int $pid) use ($pm) { - $process = new swoole_process(function (swoole_process $worker) use ($pm) { + $process = new Swoole\Process(function (Swoole\Process $worker) use ($pm) { function get(Swoole\Coroutine\Http\Client $client) { $client->get('/'); @@ -28,19 +28,19 @@ $pm->parentFunc = function (int $pid) use ($pm) { go(function () use ($cli) { $cli->get('/'); }); - swoole_event_wait(); + Swoole\Event::wait(); }, false); $process->start(); - swoole_process::wait(); + Swoole\Process::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set(['log_file' => '/dev/null']); - $server->on('workerStart', function (swoole_http_server $server) use ($pm) { + $server->on('workerStart', function (Swoole\Http\Server $server) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { co::sleep(0.1); $server->shutdown(); }); diff --git a/tests/swoole_http_client_coro/auto_reconnect.phpt b/tests/swoole_http_client_coro/auto_reconnect.phpt index 7803058c9e1..a6519e78b36 100644 --- a/tests/swoole_http_client_coro/auto_reconnect.phpt +++ b/tests/swoole_http_client_coro/auto_reconnect.phpt @@ -17,12 +17,12 @@ $pm->parentFunc = function () use ($pm) { co::sleep(0.005); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { $response->end($pm->getRandomData()); co::sleep(0.001); $server->close($request->fd); diff --git a/tests/swoole_http_client_coro/bug_2661.phpt b/tests/swoole_http_client_coro/bug_2661.phpt index 0ceb61e082d..6e22a7929f8 100644 --- a/tests/swoole_http_client_coro/bug_2661.phpt +++ b/tests/swoole_http_client_coro/bug_2661.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: #2611 bound error with dns resolve and cross close --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/connection_close.phpt b/tests/swoole_http_client_coro/connection_close.phpt new file mode 100644 index 00000000000..917ae15b296 --- /dev/null +++ b/tests/swoole_http_client_coro/connection_close.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_http_client_coro: connection close +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + var_dump($client->get('/close')); + var_dump($client->getHeaders()); + + var_dump($client->get('/keep_alive')); + var_dump($client->getHeaders()); + $client->close(); + + var_dump($client->errMsg); + echo "DONE\n"; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $server->on('request', function($request, $response) { + if ($request->server['request_uri'] == '/close') { + $response->header('connection', 'close'); + } + $response->end(); + }); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +bool(true) +array(5) { + ["connection"]=> + string(5) "close" + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) "%s" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(1) "0" +} +bool(true) +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) "%s" + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(1) "0" +} +string(0) "" +DONE diff --git a/tests/swoole_http_client_coro/cookies_set_bug.phpt b/tests/swoole_http_client_coro/cookies_set_bug.phpt index 34d88ea54f0..f8f9700d377 100644 --- a/tests/swoole_http_client_coro/cookies_set_bug.phpt +++ b/tests/swoole_http_client_coro/cookies_set_bug.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: cookies set bug --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/defer.phpt b/tests/swoole_http_client_coro/defer.phpt index 90772eb2762..bf8c30e42b2 100644 --- a/tests/swoole_http_client_coro/defer.phpt +++ b/tests/swoole_http_client_coro/defer.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $retry_time = microtime(true) - $retry_time; $pm->kill(); - usleep(1000); + $pm->wait(); // failed when connect $failed_time = microtime(true); @@ -43,11 +43,11 @@ $pm->parentFunc = function () use ($pm) { phpt_var_dump($retry_time, $failed_time); Assert::assert($retry_time > $failed_time * 2); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i <= MAX_REQUESTS) { @@ -66,6 +66,7 @@ $pm->childFunc = function () use ($pm) { } }); $server->start(); + $pm->wakeup(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_client_coro/defer_02.phpt b/tests/swoole_http_client_coro/defer_02.phpt index 55898ccb8bc..4c278ab9c79 100644 --- a/tests/swoole_http_client_coro/defer_02.phpt +++ b/tests/swoole_http_client_coro/defer_02.phpt @@ -34,12 +34,12 @@ $pm->parentFunc = function () use ($pm, $randomData) { Assert::same($cli->body, $randomData[$n]); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "OK\n"; }; $pm->childFunc = function () use ($pm, $randomData) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -47,7 +47,7 @@ $pm->childFunc = function () use ($pm, $randomData) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server, $randomData) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server, $randomData) { $response->end($randomData[$request->get['n']]); }); $server->start(); diff --git a/tests/swoole_http_client_coro/disable_keep_alive.phpt b/tests/swoole_http_client_coro/disable_keep_alive.phpt index 12c3083163e..6a809d9b98f 100644 --- a/tests/swoole_http_client_coro/disable_keep_alive.phpt +++ b/tests/swoole_http_client_coro/disable_keep_alive.phpt @@ -7,8 +7,8 @@ skip_if_offline(); --FILE-- set([ 'timeout' => 10, @@ -18,14 +18,14 @@ go(function () { $cli->get('/'); Assert::same($cli->statusCode, 200); - Assert::true($cli->get('/contract.shtml')); + Assert::true($cli->get('/ch/tech/')); Assert::same($cli->statusCode, 200); // failed clear $cli->set([ 'timeout' => 0.001 ]); - Assert::false($cli->get('/contract.shtml')); + Assert::false($cli->get('/ch/tech/')); Assert::assert(empty($cli->headers)); Assert::assert(empty($cli->body)); }); diff --git a/tests/swoole_http_client_coro/download.phpt b/tests/swoole_http_client_coro/download.phpt index 91ce84cadf0..a3abee1e74e 100644 --- a/tests/swoole_http_client_coro/download.phpt +++ b/tests/swoole_http_client_coro/download.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: download file and download offset --SKIPIF-- --FILE-- parentFunc = function (int $pid) use ($pm, &$count) { Assert::same($cli->body, $raw_file_content); }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_LOW); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set(['log_file' => '/dev/null']); $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $offset = (int) @explode('-', explode('=', $request->header['range'])[1])[0]; $response->sendfile(TEST_IMAGE, $offset); }); diff --git a/tests/swoole_http_client_coro/download_filename_bug.phpt b/tests/swoole_http_client_coro/download_filename_bug.phpt index cfca217d89a..7bf17f36ab0 100644 --- a/tests/swoole_http_client_coro/download_filename_bug.phpt +++ b/tests/swoole_http_client_coro/download_filename_bug.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function () use ($pm) { Co\run(function () use($pm) { download($pm, '/tmp/test-1.*'); }); - + Co\run(function () use($pm) { download($pm, '/tmp/test-2.*'); }); @@ -58,12 +58,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set(['log_file' => '/dev/null']); $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(TEST_IMAGE); }); $serv->start(); diff --git a/tests/swoole_http_client_coro/duplicate_header.phpt b/tests/swoole_http_client_coro/duplicate_header.phpt new file mode 100644 index 00000000000..54846f2786b --- /dev/null +++ b/tests/swoole_http_client_coro/duplicate_header.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_http_server: duplicate header +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $uuid) { + Co\run(function () use ($pm, $uuid) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->headers['values-1'], ['hello', 'swoole', $uuid]); + Assert::eq($client->headers['values-2'], ['hello', $uuid]); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $uuid) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) use ($uuid) { + $response->header('values-1', ['hello', 'swoole', $uuid]); + $response->header('values-2', ['hello', $uuid]); + $response->end('OK'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/error_handler.phpt b/tests/swoole_http_client_coro/error_handler.phpt index f334d4c5fb8..66be953b3c2 100644 --- a/tests/swoole_http_client_coro/error_handler.phpt +++ b/tests/swoole_http_client_coro/error_handler.phpt @@ -5,10 +5,15 @@ swoole_http_client_coro: error handler --FILE-- initRandomData(MAX_CONCURRENCY_MID); $pm->parentFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $cli_map = []; for ($c = MAX_CONCURRENCY_MID; $c--;) { $cli_map[] = $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); @@ -23,36 +28,28 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); $pm->wait(); - // disable reconnect so we will get the first reason (conn was closed by server side) - foreach ($cli_map as $cli) { - $cli->set(['reconnect' => false]); - $cli->setDefer(false); - Assert::assert(!$cli->get('/')); - Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); - Assert::same($cli->errCode, SOCKET_ECONNRESET); - } + System::sleep(0.2); // when we enable reconnect, we will get connect error foreach ($cli_map as $cli) { - $cli->set(['reconnect' => true]); + $cli->setDefer(false); Assert::assert(!$cli->get('/')); Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED); Assert::same($cli->errCode, SOCKET_ECONNREFUSED); } }); - swoole_event_wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', ]); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i <= MAX_CONCURRENCY_MID) { @@ -61,7 +58,7 @@ $pm->childFunc = function () use ($pm) { $server->close($request->fd); } }); - $server->on('shutdown', function (swoole_http_server $server) use ($pm) { + $server->on('shutdown', function (Server $server) use ($pm) { foreach ($server->connections as $fd) { $server->close($fd); } diff --git a/tests/swoole_http_client_coro/get.phpt b/tests/swoole_http_client_coro/get.phpt index a053f8d523c..338898eb059 100644 --- a/tests/swoole_http_client_coro/get.phpt +++ b/tests/swoole_http_client_coro/get.phpt @@ -15,18 +15,18 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null' ]); - $http->on('WorkerStart', function (\swoole_server $serv) { + $http->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $cli = new Swoole\Coroutine\Http\Client('www.qq.com', 443, true); $cli->set(['timeout' => 10]); $cli->setHeaders([ @@ -36,6 +36,7 @@ $pm->childFunc = function () use ($pm) { 'Accept-Encoding' => 'gzip', ]); $ret = ($cli->get('/')); + Assert::assert($cli->socket instanceof Swoole\Coroutine\Socket); if (!$ret) { $response->end("ERROR\n"); return; diff --git a/tests/swoole_http_client_coro/get_header_out_after_close.phpt b/tests/swoole_http_client_coro/get_header_out_after_close.phpt index 5ab29bc29dd..2b2327de638 100644 --- a/tests/swoole_http_client_coro/get_header_out_after_close.phpt +++ b/tests/swoole_http_client_coro/get_header_out_after_close.phpt @@ -17,7 +17,7 @@ co::create(function () { $http->execute('/get'); swoole_string($http->getHeaderOut())->contains('httpbin.org'); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/get_without_content_length.phpt b/tests/swoole_http_client_coro/get_without_content_length.phpt index c04757f3d22..4f49785396a 100644 --- a/tests/swoole_http_client_coro/get_without_content_length.phpt +++ b/tests/swoole_http_client_coro/get_without_content_length.phpt @@ -26,16 +26,16 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); echo "OK\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'log_file' => '/dev/null' )); - $serv->on('WorkerStart', function (\swoole_server $serv) + $serv->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager diff --git a/tests/swoole_http_client_coro/h2c_upgrade.phpt b/tests/swoole_http_client_coro/h2c_upgrade.phpt index 14e93dbd93e..3afcb8c8f90 100644 --- a/tests/swoole_http_client_coro/h2c_upgrade.phpt +++ b/tests/swoole_http_client_coro/h2c_upgrade.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set(['timeout' => 10]); $cli->setHeaders([ @@ -20,7 +20,7 @@ go(function () { ]); $ret = $cli->get('/'); Assert::assert($ret); - Assert::assert(strpos($cli->body, 'Swoole') !== false); + Assert::assert(str_contains($cli->body, 'httpbin.org')); }); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/http_chunk.phpt b/tests/swoole_http_client_coro/http_chunk.phpt new file mode 100644 index 00000000000..d9a75f9fc7e --- /dev/null +++ b/tests/swoole_http_client_coro/http_chunk.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_client_coro: http chunk +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks, $body) { + Co\run(function () use ($pm, $chunks, $body) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->get('/')); + Assert::eq($cli->getBody(), $body); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 100); + } + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/http_proxy.phpt b/tests/swoole_http_client_coro/http_proxy.phpt index 21158bd5fc3..af8d9c39619 100644 --- a/tests/swoole_http_client_coro/http_proxy.phpt +++ b/tests/swoole_http_client_coro/http_proxy.phpt @@ -10,7 +10,7 @@ skip_if_offline(); setHeaders(['Host' => $domain]); // without host header it can also work well $cli->set([ @@ -20,7 +20,7 @@ go(function () { ]); $result = $cli->get('/'); Assert::assert($result); - Assert::assert(stripos($cli->body, 'tencent') !== false); + Assert::assert(stripos($cli->body, '百度') !== false); echo "DONE\n"; }); ?> diff --git a/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt b/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt index 79a983a593e..69018dd957b 100644 --- a/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt +++ b/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt @@ -47,4 +47,4 @@ $pm->run(); GET http://127.0.0.1:1234/ HTTP/1.1 Host: 127.0.0.1:1234 Connection: keep-alive -Accept-Encoding: gzip, deflate +Accept-Encoding: gzip, deflate, br diff --git a/tests/swoole_http_client_coro/http_upload_big.phpt b/tests/swoole_http_client_coro/http_upload_big.phpt index 8b572250d44..7273cb7aca9 100644 --- a/tests/swoole_http_client_coro/http_upload_big.phpt +++ b/tests/swoole_http_client_coro/http_upload_big.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', diff --git a/tests/swoole_http_client_coro/https.phpt b/tests/swoole_http_client_coro/https.phpt index c6e2b5dc8db..8746fc325a3 100644 --- a/tests/swoole_http_client_coro/https.phpt +++ b/tests/swoole_http_client_coro/https.phpt @@ -27,7 +27,7 @@ go(function () { $cli->close(); } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- OK diff --git a/tests/swoole_http_client_coro/issue_2664.phpt b/tests/swoole_http_client_coro/issue_2664.phpt index d3e8b2323a9..4c9f902889e 100644 --- a/tests/swoole_http_client_coro/issue_2664.phpt +++ b/tests/swoole_http_client_coro/issue_2664.phpt @@ -34,11 +34,11 @@ $pm->run(); --EXPECTF-- array(4) { [0]=> - string(76) "key1=val1; expires=%s; path=/; domain=id.test.com" + string(91) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [1]=> - string(76) "key1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=test.com" + string(62) "key1=deleted; expires=%s; Max-Age=0" [2]=> - string(76) "key2=val2; %s; path=/; domain=id.test.com" + string(91) "key2=val2; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [3]=> - string(76) "key2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=test.com" + string(62) "key2=deleted; expires=%s; Max-Age=0" } diff --git a/tests/swoole_http_client_coro/keep_alive.phpt b/tests/swoole_http_client_coro/keep_alive.phpt index a295ec1ff05..ac2b7f4f3a9 100644 --- a/tests/swoole_http_client_coro/keep_alive.phpt +++ b/tests/swoole_http_client_coro/keep_alive.phpt @@ -21,7 +21,7 @@ go(function () { Assert::assert($cli->get('/')); Assert::assert(strpos($cli->body, 'pecl') !== false); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "OK\n"; ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/long_domain.phpt b/tests/swoole_http_client_coro/long_domain.phpt index b3391784055..206251ded5b 100644 --- a/tests/swoole_http_client_coro/long_domain.phpt +++ b/tests/swoole_http_client_coro/long_domain.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: long domain --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/lowercase_header.phpt b/tests/swoole_http_client_coro/lowercase_header.phpt index 7acd03d5ae2..1fb41cbd630 100644 --- a/tests/swoole_http_client_coro/lowercase_header.phpt +++ b/tests/swoole_http_client_coro/lowercase_header.phpt @@ -29,18 +29,18 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null' ]); - $http->on('WorkerStart', function (\swoole_server $serv) { + $http->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Hello-world', 'swoole', false); $response->end(); }); diff --git a/tests/swoole_http_client_coro/multi_and_reuse.phpt b/tests/swoole_http_client_coro/multi_and_reuse.phpt index 16bdabbc244..31f2afbd97e 100644 --- a/tests/swoole_http_client_coro/multi_and_reuse.phpt +++ b/tests/swoole_http_client_coro/multi_and_reuse.phpt @@ -24,7 +24,7 @@ go(function () { } $baidu = createDeferCli('www.baidu.com', true); - $qq = createDeferCli('www.qq.com', true); + $qq = createDeferCli('news.qq.com', true); //first $baidu->get('/'); @@ -38,7 +38,7 @@ go(function () { //reuse $baidu->get('/duty/'); - $qq->get('/contract.shtml'); + $qq->get('/ch/tech/'); $baidu->recv(10); $qq->recv(10); Assert::same($baidu->statusCode, 200); diff --git a/tests/swoole_http_client_coro/parser.phpt b/tests/swoole_http_client_coro/parser.phpt index 9652f3d35f4..6ceb7483089 100644 --- a/tests/swoole_http_client_coro/parser.phpt +++ b/tests/swoole_http_client_coro/parser.phpt @@ -205,17 +205,17 @@ $pm->parentFunc = function () use ($pm, &$normal_chars) { Assert::same($cli->body, $pm->getRandomData()); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { global $normal_chars; foreach ($normal_chars as $char) { $response->header($char, $pm->getRandomData()); diff --git a/tests/swoole_http_client_coro/reconnect_but_failed.phpt b/tests/swoole_http_client_coro/reconnect_but_failed.phpt index 2b3777dbcf3..e23bf4a46c4 100644 --- a/tests/swoole_http_client_coro/reconnect_but_failed.phpt +++ b/tests/swoole_http_client_coro/reconnect_but_failed.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { } $pm->kill(); - usleep(10000); + usleep(100000); Assert::assert(!$cli->get('/')); Assert::same($cli->errCode, SOCKET_ECONNREFUSED); @@ -32,19 +32,20 @@ $pm->parentFunc = function () use ($pm) { Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED); } }); - swoole_event_wait(); + Swoole\Event::wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', + 'enable_coroutine' => false, ]); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i % 2) { diff --git a/tests/swoole_http_client_coro/slow_server.phpt b/tests/swoole_http_client_coro/slow_server.phpt index 62404156fc7..a92dfeccb62 100644 --- a/tests/swoole_http_client_coro/slow_server.phpt +++ b/tests/swoole_http_client_coro/slow_server.phpt @@ -32,16 +32,16 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); echo "OK\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'log_file' => '/dev/null' )); - $serv->on('WorkerStart', function (\swoole_server $serv) + $serv->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager diff --git a/tests/swoole_http_client_coro/socks5_proxy.phpt b/tests/swoole_http_client_coro/socks5_proxy.phpt index 2da6727f005..790a57d1ae7 100644 --- a/tests/swoole_http_client_coro/socks5_proxy.phpt +++ b/tests/swoole_http_client_coro/socks5_proxy.phpt @@ -32,6 +32,6 @@ go(function () $cli->close(); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/ssl.phpt b/tests/swoole_http_client_coro/ssl.phpt index 576a9ee39ff..eb754fe3f1c 100644 --- a/tests/swoole_http_client_coro/ssl.phpt +++ b/tests/swoole_http_client_coro/ssl.phpt @@ -19,7 +19,7 @@ go(function () { Assert::assert(strpos($cli->body, $random) !== false); echo "DONE\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_http_client_coro/ssl_host_name.phpt b/tests/swoole_http_client_coro/ssl_host_name.phpt index d46c0280860..47355aee732 100644 --- a/tests/swoole_http_client_coro/ssl_host_name.phpt +++ b/tests/swoole_http_client_coro/ssl_host_name.phpt @@ -18,6 +18,6 @@ go(function () { Assert::assert(strlen($c->body) > 0); Assert::same($c->statusCode, 200); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/timeout_before_connect.phpt b/tests/swoole_http_client_coro/timeout_before_connect.phpt index 75dde8440e3..940ff3342ce 100644 --- a/tests/swoole_http_client_coro/timeout_before_connect.phpt +++ b/tests/swoole_http_client_coro/timeout_before_connect.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: use timeout and timeout before connect --SKIPIF-- --FILE-- @@ -40,6 +40,6 @@ go(function () { Assert::same($cli2->errCode, 0); Assert::assert($cli2->statusCode === 200 && strpos($cli2->body, 'tencent') !== false); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/timeout_when_recv.phpt b/tests/swoole_http_client_coro/timeout_when_recv.phpt index c9f427d43c8..a4403563c2a 100644 --- a/tests/swoole_http_client_coro/timeout_when_recv.phpt +++ b/tests/swoole_http_client_coro/timeout_when_recv.phpt @@ -20,19 +20,19 @@ $pm->parentFunc = function ($pid) use ($pm, $port) { Assert::assert(empty($cli->body)); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_http_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Http\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set([ 'log_file' => '/dev/null' ]); - $serv->on('WorkerStart', function (swoole_http_server $serv) { + $serv->on('WorkerStart', function (Swoole\Http\Server $serv) { global $pm; $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Assert::same($request->header['hello'], 'swoole'); co::sleep(2); $response->end('ok!'); diff --git a/tests/swoole_http_client_coro/unixsocket.phpt b/tests/swoole_http_client_coro/unixsocket.phpt index 1320dcb0aac..5b873b92bf7 100644 --- a/tests/swoole_http_client_coro/unixsocket.phpt +++ b/tests/swoole_http_client_coro/unixsocket.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; diff --git a/tests/swoole_http_client_coro/upload.phpt b/tests/swoole_http_client_coro/upload.phpt index f4d2ff26a69..d2c0d813a23 100644 --- a/tests/swoole_http_client_coro/upload.phpt +++ b/tests/swoole_http_client_coro/upload.phpt @@ -19,8 +19,8 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event::wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_http_client_coro/upload_huge.phpt b/tests/swoole_http_client_coro/upload_huge.phpt index 5694a6166ea..f25078ea254 100644 --- a/tests/swoole_http_client_coro/upload_huge.phpt +++ b/tests/swoole_http_client_coro/upload_huge.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { $cli = new Swoole\Coroutine\Http\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT); $cli->set(['timeout' => 10]); - $content = str_repeat(get_safe_random(IS_IN_TRAVIS ? 16 : 64), 1024 * 1024); // 64M + $content = str_repeat(get_safe_random(IS_IN_CI ? 16 : 64), 1024 * 1024); // 64M file_put_contents('/tmp/test.jpg', $content); $cli->addFile('/tmp/test.jpg', 'test.jpg'); $ret = $cli->post('/post', ['name' => 'twosee']); diff --git a/tests/swoole_http_client_coro/upload_with_null_args.phpt b/tests/swoole_http_client_coro/upload_with_null_args.phpt index f91e42b517e..e7c5b7e7f44 100644 --- a/tests/swoole_http_client_coro/upload_with_null_args.phpt +++ b/tests/swoole_http_client_coro/upload_with_null_args.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function () use ($pm) { Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; diff --git a/tests/swoole_http_client_coro/websocket/1.phpt b/tests/swoole_http_client_coro/websocket/1.phpt index 0aef3d13b36..2c8f8701c00 100644 --- a/tests/swoole_http_client_coro/websocket/1.phpt +++ b/tests/swoole_http_client_coro/websocket/1.phpt @@ -30,17 +30,17 @@ $pm->parentFunc = function ($pid) use ($pm) { co::sleep(0.1); } }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { + $ws->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ @@ -48,7 +48,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $ws->on('open', function ($serv, swoole_http_request $request) { + $ws->on('open', function ($serv, Swoole\Http\Request $request) { $ip = co::gethostbyname('www.baidu.com'); if ($ip) { diff --git a/tests/swoole_http_client_coro/websocket/bug_01.phpt b/tests/swoole_http_client_coro/websocket/bug_01.phpt index 4eebef0b56c..07216de64ba 100644 --- a/tests/swoole_http_client_coro/websocket/bug_01.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_01.phpt @@ -6,34 +6,32 @@ swoole_http_client_coro/websocket: handshake + frame parentFunc = function ($pid) use ($pm) { - go(function () use ($pm) { + Co\run(function () use ($pm) { $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); $ret = $cli->upgrade('/'); - if (!$ret) - { + if (!$ret) { echo "ERROR\n"; return; } echo "CONNECTED\n"; echo $cli->recv()->data; }); - swoole_event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { - /** - * @var $pm ProcessManager - */ + $ws->on('WorkerStart', function (Server $serv) { global $pm; $pm->wakeup(); }); @@ -42,7 +40,7 @@ $pm->childFunc = function () use ($pm) $sendData = "HTTP/1.1 101 Switching Protocols\r\n"; $sendData .= "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\r\n"; $sendData .= "Sec-Websocket-Version: 13\r\nServer: swoole-http-server\r\n\r\n"; - $sendData .= swoole_websocket_server::pack("hello world\n"); + $sendData .= WebSockerServer::pack("hello world\n"); $serv->send($fd, $sendData); }); diff --git a/tests/swoole_http_client_coro/websocket/bug_02.phpt b/tests/swoole_http_client_coro/websocket/bug_02.phpt index 25d1e854e1a..da334153058 100644 --- a/tests/swoole_http_client_coro/websocket/bug_02.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_02.phpt @@ -20,27 +20,27 @@ $pm->parentFunc = function () use ($pm) { } $cli->close(); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort()); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $ws->set([ 'log_file' => '/dev/null', 'worker_num' => 1 ]); - $ws->on('workerStart', function (swoole_websocket_server $serv) use ($pm) { + $ws->on('workerStart', function (Swoole\WebSocket\Server $serv) use ($pm) { $pm->wakeup(); }); - $ws->on('open', function (swoole_websocket_server $ws, swoole_http_request $request) { + $ws->on('open', function (Swoole\WebSocket\Server $ws, Swoole\Http\Request $request) { $ws->push($request->fd, "server: hello, welcome\n"); }); - $ws->on('message', function (swoole_websocket_server $ws, swoole_websocket_frame $frame) { + $ws->on('message', function (Swoole\WebSocket\Server $ws, Swoole\WebSocket\Frame $frame) { echo "client: {$frame->data}"; $frame->data = str_replace('server', 'client', $frame->data); $ws->push($frame->fd, "server-reply: {$frame->data}"); }); - $ws->on('close', function (swoole_websocket_server $ws, int $fd) { + $ws->on('close', function (Swoole\WebSocket\Server $ws, int $fd) { echo "client-{$fd} is closed\n"; }); $ws->start(); diff --git a/tests/swoole_http_client_coro/websocket/close_socket.phpt b/tests/swoole_http_client_coro/websocket/close_socket.phpt new file mode 100644 index 00000000000..a5ca32ffa34 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/close_socket.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_http_client_coro/websocket: close socket +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => -1]); + $cli->setHeaders([]); + $ret = $cli->upgrade('/'); + if (!$ret) { + echo "ERROR\n"; + return; + } + Assert::assert($cli->socket->close()); + Assert::false($cli->recv()); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); + Assert::false($cli->push('hello server')); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); + }); + Swoole\Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws->set(array( + 'log_file' => '/dev/null' + )); + $ws->on('WorkerStart', function (Swoole\Server $serv) { + global $pm; + $pm->wakeup(); + }); + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + + }); + $ws->on('message', function ($serv, $frame) { + $serv->push($frame->fd, "hello client\n"); + }); + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_client_coro/websocket/server_push_first.phpt b/tests/swoole_http_client_coro/websocket/server_push_first.phpt index 7981a579e19..4b404dab1d5 100644 --- a/tests/swoole_http_client_coro/websocket/server_push_first.phpt +++ b/tests/swoole_http_client_coro/websocket/server_push_first.phpt @@ -36,8 +36,9 @@ Co\run(function () use ($pm) { go(function () use ($pm, $server) { $wr = WaitRef::create(); + $childs = []; for ($c = MAX_CONCURRENCY_LOW; $c--;) { - go(function () use ($pm, $wr) { + $childs[] = go(function () use ($pm, $wr) { $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/websocket'); @@ -49,9 +50,11 @@ Co\run(function () use ($pm) { }); } WaitRef::wait($wr); + echo "DONE\n"; $server->shutdown(); }); }); ?> --EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/websocket/timeout.phpt b/tests/swoole_http_client_coro/websocket/timeout.phpt index 8d177d1a434..b1096bc3b3a 100644 --- a/tests/swoole_http_client_coro/websocket/timeout.phpt +++ b/tests/swoole_http_client_coro/websocket/timeout.phpt @@ -28,17 +28,17 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::false($cli->recv(.1)); Assert::same($cli->errCode, SOCKET_ETIMEDOUT); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { + $ws->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ @@ -46,7 +46,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $ws->on('open', function ($serv, swoole_http_request $request) { + $ws->on('open', function ($serv, Swoole\Http\Request $request) { $serv->push($request->fd, "start\n"); }); diff --git a/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt b/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt new file mode 100644 index 00000000000..48bba417de9 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_client_coro/websocket: client & server +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort(), false); + $client->setHeaders([ + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + + Assert::assert($client->get('/')); + echo $client->getBody(); + + Assert::assert($client->upgrade('/')); + + echo $client->recv(2)->data; + $client->push("hello"); + echo $client->recv(2)->data; + + $client->close(); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws->set(array( + 'log_file' => '/dev/null' + )); + $ws->on('WorkerStart', function (Swoole\Server $serv) { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + $serv->push($request->fd, "msg 1\n"); + }); + + $ws->on('message', function ($serv, $frame) { + co::sleep(0.1); + $serv->push($frame->fd, "msg 2\n"); + }); + + $ws->on('request', function ($req, $resp) { + $resp->end("OK\n"); + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK +msg 1 +msg 2 diff --git a/tests/swoole_http_client_coro/write_func_1.phpt b/tests/swoole_http_client_coro/write_func_1.phpt new file mode 100644 index 00000000000..51f668b9a86 --- /dev/null +++ b/tests/swoole_http_client_coro/write_func_1.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_http_client_coro: write func 1 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $index = 0; + $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) { + Assert::eq($chunks[$index], $data); + $index++; + }]); + Assert::assert($cli->get('/')); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 1000); + } + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/write_func_2.phpt b/tests/swoole_http_client_coro/write_func_2.phpt new file mode 100644 index 00000000000..e2da0b38d83 --- /dev/null +++ b/tests/swoole_http_client_coro/write_func_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_client_coro: write func 1 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $index = 0; + $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) { + Assert::eq($chunks[$index], $data); + $index++; + if ($index == N / 2) { + // reset connection + $client->close(); + } + }]); + Assert::false($cli->get('/')); + Assert::eq($cli->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 1000); + } + $resp->end(); + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/0.phpt b/tests/swoole_http_server/0.phpt index e6d008681da..0970e441ef6 100644 --- a/tests/swoole_http_server/0.phpt +++ b/tests/swoole_http_server/0.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/If_Modified_Since.phpt b/tests/swoole_http_server/If_Modified_Since.phpt new file mode 100644 index 00000000000..9b13f92e4f3 --- /dev/null +++ b/tests/swoole_http_server/If_Modified_Since.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server: If-Modified-Since +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg"); + $lastModified = $response['headers']['last-modified']; + Assert::same($response['statusCode'], 200); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['headers' => ['-If-Modified-Since' => 'aaaa', 'If-Modified-Since' => $lastModified]]); + Assert::same($response['statusCode'], 304); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/buffer_output_size.phpt b/tests/swoole_http_server/buffer_output_size.phpt index 620fdc51474..449f471cd7b 100644 --- a/tests/swoole_http_server/buffer_output_size.phpt +++ b/tests/swoole_http_server/buffer_output_size.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { 'http_compression' => false, 'output_buffer_size' => OUTPUT_BUFFER_SIZE, ]); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { $length = $request->server['request_uri'] === '/full' ? OUTPUT_BUFFER_SIZE + 4096 : OUTPUT_BUFFER_SIZE - HTTP_HEADER_SIZE; $response->end(str_repeat(RANDOM_CHAR, $length)); }); diff --git a/tests/swoole_http_server/bug_2368.phpt b/tests/swoole_http_server/bug_2368.phpt index 6054b820778..281aaa43aa5 100644 --- a/tests/swoole_http_server/bug_2368.phpt +++ b/tests/swoole_http_server/bug_2368.phpt @@ -18,16 +18,16 @@ $pm->parentFunc = function () use ($pm) { ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort()); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(array( 'log_file' => '/dev/null', )); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('name', COOKIE); $response->end(); }); diff --git a/tests/swoole_http_server/bug_2444.phpt b/tests/swoole_http_server/bug_2444.phpt index ff6357a7f2e..cd4b6067b4c 100644 --- a/tests/swoole_http_server/bug_2444.phpt +++ b/tests/swoole_http_server/bug_2444.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['log_file' => '/dev/null']); + $server->set(['log_file' => '/dev/null', 'hook_flags' => SWOOLE_HOOK_ALL]); $server->on('start', function () use ($pm) { $pm->wakeup(); }); @@ -30,18 +30,16 @@ $pm->childFunc = function () use ($pm) { return; } $cli->close(); - $db = new Swoole\Coroutine\Mysql(); - if (!Assert::assert($db->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]))) { + $db = new mysqli(); + $db->set_opt(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); + if (!Assert::assert($db->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT))) { goto _error; } - if (!Assert::assert($db->query('select 1')[0][1] === 1)) { + if (!Assert::assert($db->query('select 1')->fetch_all()[0][0] === 1)) { goto _error; } $db->close(); diff --git a/tests/swoole_http_server/bug_2988.phpt b/tests/swoole_http_server/bug_2988.phpt index 839c5e093e4..ee4eab6fd7c 100644 --- a/tests/swoole_http_server/bug_2988.phpt +++ b/tests/swoole_http_server/bug_2988.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_http_server/bug_4261.phpt b/tests/swoole_http_server/bug_4261.phpt new file mode 100644 index 00000000000..d5582b0e6fa --- /dev/null +++ b/tests/swoole_http_server/bug_4261.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_http_server: bug Github#4261 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $uuid = urlencode(uniqid('swoole')); + $port = $pm->getFreePort(); + $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'"); + Assert::contains($out, $uuid); + $pm->kill(); + echo "SUCCESS\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set(['log_file' => '/dev/null']); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->end(var_export($request->post, true)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); + +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_http_server/bug_4857.phpt b/tests/swoole_http_server/bug_4857.phpt new file mode 100644 index 00000000000..6abded5af23 --- /dev/null +++ b/tests/swoole_http_server/bug_4857.phpt @@ -0,0 +1,104 @@ +--TEST-- +swoole_http_server: bug Github#4857 Invalid "Transfer-Encoding: chunked" header appended +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + + // without special content-length + $headers = httpGetHeaders( + "http://127.0.0.1:{$pm->getFreePort()}?encoding=1", + [ + 'headers' => ['Accept-Encoding' => 'gzip, br'], + ] + ); + var_dump($headers); + + // without content-length + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + var_dump($headers); + + // with content-length + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}?normal=1"); + var_dump($headers); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $data = '宛如繁星般,宛如皎月般'; + if (isset($request->get['normal'])) { + $response->header('Content-Length', mb_strlen($data)); + $response->end($data); + } elseif (isset($request->get['encoding'])) { + $response->header('Content-Length', 1000); + $response->end($data); + } else { + $response->header('Content-Length', 100); + $response->write($data); + $response->end(); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +%s +array(6) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-encoding"]=> + string(%d) %s + ["content-length"]=> + string(%d) %s +} +%s +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["transfer-encoding"]=> + string(7) "chunked" +} +%s +array(6) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-encoding"]=> + string(%d) %s + ["content-length"]=> + string(%d) %s +} +DONE diff --git a/tests/swoole_http_server/bug_5107.phpt b/tests/swoole_http_server/bug_5107.phpt new file mode 100644 index 00000000000..5e071f96071 --- /dev/null +++ b/tests/swoole_http_server/bug_5107.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_server: bug Github#5107 Error response status +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + var_dump($headers); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->status(200, "status"); + $response->end("Hello World"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(2) "11" +} +DONE diff --git a/tests/swoole_http_server/bug_5114.phpt b/tests/swoole_http_server/bug_5114.phpt new file mode 100644 index 00000000000..43d1d1fbf92 --- /dev/null +++ b/tests/swoole_http_server/bug_5114.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_http_server: bug #5114 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/http/UPPER.TXT"); + Assert::same($response['statusCode'], 200); + Assert::same($response['headers']['content-type'], 'text/plain'); + Assert::same($response['body'], "HELLO WORLD!\n"); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/http/test.txt"); + Assert::same($response['statusCode'], 200); + Assert::same($response['headers']['content-type'], 'text/plain'); + Assert::same($response['body'], "hello world!\n"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/bug_5146.phpt b/tests/swoole_http_server/bug_5146.phpt new file mode 100644 index 00000000000..2efbbc39dc1 --- /dev/null +++ b/tests/swoole_http_server/bug_5146.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_http_server: Github#5146 HTTP服务器,添加响应cookie时,如果设置了过期时间会内存泄漏 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $previous = memory_get_usage(); + $response->cookie( + 'test_cookie', + 'hello', + time() + (24 * 60 * 60) + ); + + global $previous; + global $item; + $current = memory_get_usage(); + $stats = [ + 'id' => $http->getWorkerId(), + 'item' => $item++, + 'prev_mem' => $previous, + 'curr_mem' => $current, + 'diff_mem' => $current - $previous, + ]; + $previous = $current; + + echo json_encode($stats), PHP_EOL; + $response->end('test response'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +{"id":%d,"item":null,"prev_mem":null,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +DONE diff --git a/tests/swoole_http_server/bug_5186.phpt b/tests/swoole_http_server/bug_5186.phpt new file mode 100644 index 00000000000..b6672c216ea --- /dev/null +++ b/tests/swoole_http_server/bug_5186.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_http_server: GitHub issue #5186 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $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"; + $client = new Client(SWOOLE_SOCK_TCP); + $client->connect('127.0.0.1', $pm->getFreePort(), 0.5); + $client->send($content); + $client->close(); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + var_dump($request->files); + $response->end('Hello World'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE +NULL diff --git a/tests/swoole_http_server/bug_compression_level.phpt b/tests/swoole_http_server/bug_compression_level.phpt index e8f2085ba0b..78cd2f214ea 100644 --- a/tests/swoole_http_server/bug_compression_level.phpt +++ b/tests/swoole_http_server/bug_compression_level.phpt @@ -36,7 +36,7 @@ for ($level = MIN_COMPRESSION_LEVEL; $level <= MAX_COMPRESSION_LEVEL; $level++) }; $pm->childFunc = function () use ($pm, $level, $randomBytes) { phpt_var_dump($level); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', 'http_compression' => true, diff --git a/tests/swoole_http_server/callback_new_obj_method.phpt b/tests/swoole_http_server/callback_new_obj_method.phpt index bff2cf4e5a2..aea20ab44e3 100644 --- a/tests/swoole_http_server/callback_new_obj_method.phpt +++ b/tests/swoole_http_server/callback_new_obj_method.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; @@ -26,7 +26,7 @@ $pm->childFunc = function () use ($pm) { class TestCo { - public function foo(swoole_http_request $request, swoole_http_response $response) + public function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm) { } } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_new_static_method.phpt b/tests/swoole_http_server/callback_new_static_method.phpt index 724acf2ad2d..d8cf60bf663 100644 --- a/tests/swoole_http_server/callback_new_static_method.phpt +++ b/tests/swoole_http_server/callback_new_static_method.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { { private static $test = ''; - public static function foo(swoole_http_request $request, swoole_http_response $response) + public static function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) { } } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_string.phpt b/tests/swoole_http_server/callback_string.phpt index 57a3d8e6470..e66f00c7ad8 100644 --- a/tests/swoole_http_server/callback_string.phpt +++ b/tests/swoole_http_server/callback_string.phpt @@ -18,13 +18,13 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - function foo(swoole_http_request $request, swoole_http_response $response) + function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { static $test = ''; co::sleep(0.001); @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { echo $test; } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_with_internal_function.phpt b/tests/swoole_http_server/callback_with_internal_function.phpt index 6c7bd4b97f4..ddf723da487 100644 --- a/tests/swoole_http_server/callback_with_internal_function.phpt +++ b/tests/swoole_http_server/callback_with_internal_function.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_with_private.phpt b/tests/swoole_http_server/callback_with_private.phpt index 61d07408227..e436119df06 100644 --- a/tests/swoole_http_server/callback_with_private.phpt +++ b/tests/swoole_http_server/callback_with_private.phpt @@ -13,7 +13,7 @@ $pm->childFunc = function () use ($pm) { class TestCo_9 { - private function foo(swoole_http_request $request, swoole_http_response $response) + private function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -38,6 +38,6 @@ $pm->run(true); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -Assert::contains($output, 'Swoole\Server::on() must be callable'); +Assert::contains($output, "Swoole\Server\Port::on(): function 'TestCo_9::foo' is not callable"); ?> --EXPECT-- diff --git a/tests/swoole_http_server/callback_with_protected.phpt b/tests/swoole_http_server/callback_with_protected.phpt index fb5d0ad2d49..bf645080af7 100644 --- a/tests/swoole_http_server/callback_with_protected.phpt +++ b/tests/swoole_http_server/callback_with_protected.phpt @@ -5,13 +5,13 @@ swoole_http_server: http server with protected callback --FILE-- setWaitTimeout(0); -$pm->parentFunc = function () { }; -$pm->childFunc = function () use ($pm) { + +use SwooleTest\ProcessManager; + +$pm = ProcessManager::exec(function ($pm) { class TestCo { - protected function foo(swoole_http_request $request, swoole_http_response $response) + protected function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -30,12 +30,10 @@ $pm->childFunc = function () use ($pm) { ]); $http->on('request', [new TestCo, 'foo']); $http->start(); -}; -$pm->childFirst(); -$pm->run(true); +}); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -Assert::contains($output, 'Swoole\Server::on() must be callable'); +Assert::contains($output, 'Swoole\Server\Port::on(): function \'TestCo::foo\' is not callable'); ?> --EXPECT-- diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index fce2ca85869..c85da9073cd 100644 --- a/tests/swoole_http_server/chunk.phpt +++ b/tests/swoole_http_server/chunk.phpt @@ -12,7 +12,7 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); Assert::assert(!empty($data)); - Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + Assert::eq(md5($data), md5_file(TEST_IMAGE)); $pm->kill(); }); Swoole\Event::wait(); @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ //'log_file' => '/dev/null', @@ -31,13 +31,12 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $data = str_split(file_get_contents(TEST_IMAGE), 8192); - foreach ($data as $chunk) - { - $response->write($chunk); + foreach ($data as $chunk) { + Assert::true($response->write($chunk)); } - $response->end(); + Assert::true($response->end()); }); $http->start(); diff --git a/tests/swoole_http_server/chunk_with_end_data.phpt b/tests/swoole_http_server/chunk_with_end_data.phpt new file mode 100644 index 00000000000..369d02bc897 --- /dev/null +++ b/tests/swoole_http_server/chunk_with_end_data.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server: send data in the end method with chunked encoding +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); + Assert::assert(!empty($data)); + Assert::eq(md5($data), md5_file(TEST_IMAGE)); + $pm->kill(); + }); + Swoole\Event::wait(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + + $http->set([ + //'log_file' => '/dev/null', + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $data = str_split(file_get_contents(TEST_IMAGE), 8192); + foreach ($data as $k => $chunk) { + if ($k == count($data) - 1) { + break; + } + $response->write($chunk); + } + $response->end($data[count($data) - 1]); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/chunked_pipeline_request.phpt b/tests/swoole_http_server/chunked_pipeline_request.phpt index 8a4ca4bf154..deada583aa2 100644 --- a/tests/swoole_http_server/chunked_pipeline_request.phpt +++ b/tests/swoole_http_server/chunked_pipeline_request.phpt @@ -4,136 +4,18 @@ swoole_http_server: chunked and pipeline request --FILE-- initRandomData(1); $pm->parentFunc = function () use ($pm) { - Coroutine\run(function () use ($pm) { - $request_empty_chunked = - "DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\r\n" . - "x-real-ip: 10.2.100.1\r\n" . - "x-forwarded-server: kitchen.pool-x.net\r\n" . - "accept: application/json\r\n" . - "origin: http://pool-x.net\r\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" . - "sec-fetch-site: cross-site\r\n" . - "sec-fetch-mode: cors\r\n" . - "referer: http://pool-x.net/assets/staking\r\n" . - "accept-encoding: gzip, deflate, br\r\n" . - "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\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" . - "x-domain: kitchen.pool-x.net\r\n" . - "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . - "x-device-id: \r\n" . - "x-origin-domain-id: pool\r\n" . - "x-forwarded-proto: http\r\n" . - "uber-trace-id: ffaf3497a6deee40%3A8afa1564e1e0783f%3Affaf3497a6deee40%3A1\r\n" . - "x-forwarded-port: 80\r\n" . - "x-forwarded-for: 127.0.0.1\r\n" . - "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . - "x-domain-id: pool\r\n" . - "kyc-country: \r\n" . - "kyc-status: \r\n" . - "x-forwarded-host: kitchen.pool-x.net\r\n" . - "x-forwarded-prefix: /pool-staking\r\n" . - "gateway-type: WEB\r\n" . - "lang: zh_CN\r\n" . - "Transfer-Encoding: chunked\r\n" . - "Host: 10.2.1.51:9526\r\n" . - "Connection: Keep-Alive\r\n" . - "\r\n" . - "0\r\n" . - "\r\n"; - $request_zero_length = - "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" . - "x-real-ip: 10.2.100.1\r\n" . - "x-forwarded-server: kitchen.pool-x.net\r\n" . - "accept: application/json\r\n" . - "origin: http://pool-x.net\r\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" . - "sec-fetch-site: cross-site\r\n" . - "sec-fetch-mode: cors\r\n" . - "referer: http://pool-x.net/assets/staking\r\n" . - "accept-encoding: gzip, deflate, br\r\n" . - "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\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" . - "x-domain: kitchen.pool-x.net\r\n" . - "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . - "x-device-id: \r\n" . - "x-origin-domain-id: pool\r\n" . - "x-forwarded-proto: http\r\n" . - "uber-trace-id: df854c374e6d4fde%3Ada6b1dc2e4e112b5%3Adf854c374e6d4fde%3A0\r\n" . - "x-forwarded-port: 80\r\n" . - "x-forwarded-for: 127.0.0.1\r\n" . - "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . - "x-domain-id: pool\r\n" . - "kyc-country: \r\n" . - "kyc-status: \r\n" . - "x-forwarded-host: kitchen.pool-x.net\r\n" . - "x-forwarded-prefix: /pool-staking\r\n" . - "gateway-type: WEB\r\n" . - "lang: zh_CN\r\n" . - "Content-Length: 0\r\n" . - "Host: 10.2.1.51:9526\r\n" . - "Connection: Keep-Alive\r\n" . - "\r\n"; - $request_chunked_body_array = ['FOO', 'BAR', 'CHAR', str_repeat('Z', mt_rand(10, 1000))]; - $request_chunked_body = generateChunkBody($request_chunked_body_array); - $request_chunked = "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n{$request_chunked_body}\r\n"; - $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - Assert::true($socket->connect('127.0.0.1', $pm->getFreePort())); - Assert::true($socket->setProtocol([ - 'open_eof_check' => true, - 'package_eof' => EOF - ])); - /* chunked */ - $ret = $socket->sendAll($request_empty_chunked); - Assert::same($ret, strlen($request_empty_chunked)); - $ret = $socket->recvPacket(); - Assert::isEmpty(getHttpBody($ret)); - /* pipeline */ - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->sendAll($request_zero_length); - Assert::same($ret, strlen($request_zero_length)); - } - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->recvPacket(); - Assert::same(getHttpBody($ret), getHttpBody($request_zero_length)); - } - /* chunked */ - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->sendAll($request_chunked); - Assert::same($ret, strlen($request_chunked)); - $ret = $socket->recvPacket(); - Assert::same(getHttpBody($ret), implode('', $request_chunked_body_array)); - } - }); - echo "SUCCESS\n"; - $pm->kill(); + chunked_request($pm); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', // 'log_level' => SWOOLE_LOG_DEBUG, diff --git a/tests/swoole_http_server/co_switching.phpt b/tests/swoole_http_server/co_switching.phpt index 38599833bd4..cc107551bef 100644 --- a/tests/swoole_http_server/co_switching.phpt +++ b/tests/swoole_http_server/co_switching.phpt @@ -18,17 +18,17 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', 'worker_num' => swoole_cpu_num() ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { go(function () { for ($i = 5; $i--;) { co::sleep(0.001); diff --git a/tests/swoole_http_server/compression.phpt b/tests/swoole_http_server/compression.phpt index 3f8f4ffe642..2c285f02f9f 100644 --- a/tests/swoole_http_server/compression.phpt +++ b/tests/swoole_http_server/compression.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $http->set([ 'http_gzip_level' => 9, @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); diff --git a/tests/swoole_http_server/compression_min_length.phpt b/tests/swoole_http_server/compression_min_length.phpt index 1273c4f12b3..25bf5912665 100644 --- a/tests/swoole_http_server/compression_min_length.phpt +++ b/tests/swoole_http_server/compression_min_length.phpt @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(str_repeat('A', $request->get['bytes'])); }); $http->start(); diff --git a/tests/swoole_http_server/compression_types.phpt b/tests/swoole_http_server/compression_types.phpt new file mode 100644 index 00000000000..74d5df30f59 --- /dev/null +++ b/tests/swoole_http_server/compression_types.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http_compression_types_test($pm); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + ]); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/cookieAlias.phpt b/tests/swoole_http_server/cookieAlias.phpt new file mode 100644 index 00000000000..57946ffa6aa --- /dev/null +++ b/tests/swoole_http_server/cookieAlias.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http_cookie: cookie alias +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\Run(function () use ($pm) { + var_dump(httpRequest("http://127.0.0.1:{$pm->getFreePort()}")['set_cookie_headers']); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $cookie = new Swoole\Http\Cookie(); + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + $response->setCookie($cookie); + $response->setCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true); + $response->setRawCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true); + + $cookie->withValue(''); + $response->setCookie($cookie); + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(4) { + [0]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [1]=> + string(152) "key1=val1; expires=%s; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [2]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [3]=> + string(62) "key1=deleted; expires=%s; Max-Age=0" +} diff --git a/tests/swoole_http_server/cookie_delete.phpt b/tests/swoole_http_server/cookie_delete.phpt index f5f2dc39c1f..59421cc3eb6 100644 --- a/tests/swoole_http_server/cookie_delete.phpt +++ b/tests/swoole_http_server/cookie_delete.phpt @@ -14,25 +14,25 @@ $pm->parentFunc = function () use ($pm) { Assert::same($cli->statusCode, 200); Assert::assert($cli->set_cookie_headers === [ - 'cookie1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', + 'cookie1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', 'cookie3=cookie3', 'cookie4=cookie4', - 'cookie5=cookie5; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie6=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie7=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie8=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', + 'cookie5=cookie5; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie6=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie7=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie8=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('cookie1', null); $response->cookie('cookie2', ''); $response->cookie('cookie3', 'cookie3', 0); // must be > 0 diff --git a/tests/swoole_http_server/cookie_samesite.phpt b/tests/swoole_http_server/cookie_samesite.phpt index 10e004a8a05..17b6707ee09 100644 --- a/tests/swoole_http_server/cookie_samesite.phpt +++ b/tests/swoole_http_server/cookie_samesite.phpt @@ -12,18 +12,18 @@ $pm->parentFunc = function () use ($pm) { $cli->get('/'); Assert::assert($cli->set_cookie_headers === [ - 'a=123; samesite=Lax', + 'a=123; SameSite=Lax', ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('a', '123', 0, '', '', false, false, 'Lax'); $response->end(); }); diff --git a/tests/swoole_http_server/cookie_vs_rawcookie.phpt b/tests/swoole_http_server/cookie_vs_rawcookie.phpt index ab8c76e31d9..3767d198460 100644 --- a/tests/swoole_http_server/cookie_vs_rawcookie.phpt +++ b/tests/swoole_http_server/cookie_vs_rawcookie.phpt @@ -10,14 +10,13 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cookie = '123_,; abc'; - Assert::assert($cli->get('/?cookie=' . urlencode($cookie))); + $cookie_encoded = urlencode($cookie); + Assert::assert($cli->get('/?cookie=' . $cookie_encoded)); Assert::same($cli->statusCode, 200); - Assert::assert($cli->set_cookie_headers === - [ - 'cookie=' . urlencode($cookie), - 'rawcookie=' . $cookie, - ] - ); + Assert::eq($cli->set_cookie_headers, [ + 'cookie=' . $cookie_encoded, + 'rawcookie=' . $cookie_encoded, + ]); }); for ($i = MAX_CONCURRENCY_LOW; $i--;) { go(function () use ($pm) { @@ -33,17 +32,17 @@ $pm->parentFunc = function () use ($pm) { ); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { - $request->get['cookie'] = urldecode($request->get['cookie']); - $response->cookie('cookie', $request->get['cookie']); - $response->rawcookie('rawcookie', $request->get['cookie']); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $cookie = $request->get['cookie']; + $response->cookie('cookie', $cookie); + $response->rawcookie('rawcookie', urlencode($cookie)); $response->end(); }); $http->start(); @@ -51,5 +50,5 @@ $pm->childFunc = function () use ($pm) { $pm->childFirst(); $pm->run(); ?> ---EXPECT-- +--EXPECTF-- SUCCESS diff --git a/tests/swoole_http_server/cookies.phpt b/tests/swoole_http_server/cookies.phpt index b408d5286c1..bee00c8ce0f 100644 --- a/tests/swoole_http_server/cookies.phpt +++ b/tests/swoole_http_server/cookies.phpt @@ -29,7 +29,7 @@ $cookies = array ( $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm, $cookies) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) { exit("connect failed. Error: {$client->errCode}\n"); @@ -62,11 +62,11 @@ $pm->parentFunc = function ($pid) use ($pm, $cookies) { $respCookie = json_decode($_respCookieStr, true); Assert::same($respCookie, $cookies); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); @@ -75,7 +75,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->cookie)); }); diff --git a/tests/swoole_http_server/cookies_parse.phpt b/tests/swoole_http_server/cookies_parse.phpt new file mode 100644 index 00000000000..d65030b6b46 --- /dev/null +++ b/tests/swoole_http_server/cookies_parse.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: cookies parse +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort(), false); + $client->setHeaders([ + 'Cookie' => 'PHPSESSID=5359a08f4ddbf825f0e99a3393e5dc9e; HttpOnly; q=URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ' + ]); + $client->get('/'); + $client->close(); + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->on("Start", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($request, $response) use ($serv){ + var_dump($request->cookie); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(3) { + ["PHPSESSID"]=> + string(32) "5359a08f4ddbf825f0e99a3393e5dc9e" + ["HttpOnly"]=> + string(0) "" + ["q"]=> + string(40) "URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ" +} diff --git a/tests/swoole_http_server/data_parse.phpt b/tests/swoole_http_server/data_parse.phpt index 79fcef15f5e..67d484bd99d 100644 --- a/tests/swoole_http_server/data_parse.phpt +++ b/tests/swoole_http_server/data_parse.phpt @@ -100,13 +100,13 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("WorkerStart", function ($serv, $wid) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { $response->end(var_dump_return($request->get, $request->post)); $http->close($request->fd); }); diff --git a/tests/swoole_http_server/disable_compression.phpt b/tests/swoole_http_server/disable_compression.phpt new file mode 100644 index 00000000000..8bd47e68bca --- /dev/null +++ b/tests/swoole_http_server/disable_compression.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: disable compression +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq(md5_file(__DIR__ . '/../../README.md'), md5($client->getBody())); + Assert::keyNotExists($client->headers, 'content-encoding'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) { + // Set Content-Encoding header to empty to disable compression + $response->header('Content-Encoding', ''); + $response->end(co::readFile(__DIR__ . '/../../README.md')); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/dispatch_mode_7.phpt b/tests/swoole_http_server/dispatch_mode_7.phpt index 665a065db0f..cc127ad28d0 100644 --- a/tests/swoole_http_server/dispatch_mode_7.phpt +++ b/tests/swoole_http_server/dispatch_mode_7.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server("0.0.0.0", $pm->getFreePort()); + $http = new Swoole\Http\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'reactor_num' => 2, diff --git a/tests/swoole_http_server/duplicate_header.phpt b/tests/swoole_http_server/duplicate_header.phpt index 2cb5b495644..37743d67154 100644 --- a/tests/swoole_http_server/duplicate_header.phpt +++ b/tests/swoole_http_server/duplicate_header.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'enable_coroutine' => false, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $msg = "hello world"; $response->header("content-length", strlen($msg) . " "); $response->header("Test-Value", [ @@ -58,8 +58,8 @@ Test-Value: e Test-Value: 5678 Test-Value: 3.1415926 Server: swoole-http-server +Date: %s Connection: keep-alive Content-Type: text/html -Date: %s hello world diff --git a/tests/swoole_http_server/enable_coroutine.phpt b/tests/swoole_http_server/enable_coroutine.phpt index ef1ca59552c..8453ff6bb66 100644 --- a/tests/swoole_http_server/enable_coroutine.phpt +++ b/tests/swoole_http_server/enable_coroutine.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'enable_coroutine' => false, // close build-in coroutine 'worker_num' => 1, diff --git a/tests/swoole_http_server/error_1203.phpt b/tests/swoole_http_server/error_1203.phpt index e8460db4dbc..914995ddb9d 100644 --- a/tests/swoole_http_server/error_1203.phpt +++ b/tests/swoole_http_server/error_1203.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'http_compression' => false, @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { Assert::eq($response->end(str_repeat('A', 256 * 1024)), false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE); }); diff --git a/tests/swoole_http_server/event_stream.phpt b/tests/swoole_http_server/event_stream.phpt new file mode 100644 index 00000000000..96ea8b132a4 --- /dev/null +++ b/tests/swoole_http_server/event_stream.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_server: event stream +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $data) { + Co\run(function () use ($pm, $data) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::isEmpty($client->getBody()); + Assert::keyNotExists($client->headers, 'content-length'); + Assert::eq($client->headers['content-type'], "text/event-stream"); + for ($i = 0; $i < N; $i++) { + Co::sleep(0.01); + $line1 = $client->socket->recvLine(); + $line2 = $client->socket->recvLine(); + Assert::eq($line1 . $line2, $data[$i]); + } + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $data) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('WorkerStart', function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http, $data) { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + Co::sleep(0.05); + for ($i = 0; $i < N; $i++) { + Co::sleep(0.01); + $http->send($resp->fd, $data[$i]); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/form_data_1.phpt b/tests/swoole_http_server/form_data_1.phpt new file mode 100644 index 00000000000..88493f4fc6c --- /dev/null +++ b/tests/swoole_http_server/form_data_1.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: form data 1 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test($pm, [250]); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set(['log_file' => '/dev/null']); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Request $request, Response $response) use ($http) { + $response->end(json_encode($request->post)); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/form_data_with_charset.phpt b/tests/swoole_http_server/form_data_with_charset.phpt index 3713b666be1..0d044e61af5 100644 --- a/tests/swoole_http_server/form_data_with_charset.phpt +++ b/tests/swoole_http_server/form_data_with_charset.phpt @@ -91,7 +91,7 @@ $pm->childFunc = function () use ($pm) { $http->on('WorkerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('Request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { $response->end(var_dump_return($request->get, $request->post)); $http->send($request->fd, EOF); }); diff --git a/tests/swoole_http_server/headers_sent.phpt b/tests/swoole_http_server/headers_sent.phpt index ec994abcd36..26b5c1ef406 100644 --- a/tests/swoole_http_server/headers_sent.phpt +++ b/tests/swoole_http_server/headers_sent.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, 'enable_coroutine' => false, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { ob_start(); echo 'Test'; $output = ob_get_clean(); diff --git a/tests/swoole_http_server/headers_sent_coroutine.phpt b/tests/swoole_http_server/headers_sent_coroutine.phpt index 8b5d84ea379..be0a89a7eb7 100644 --- a/tests/swoole_http_server/headers_sent_coroutine.phpt +++ b/tests/swoole_http_server/headers_sent_coroutine.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, 'enable_coroutine' => true, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { ob_start(); echo 'Test'; $output = ob_get_clean(); diff --git a/tests/swoole_http_server/https.phpt b/tests/swoole_http_server/https.phpt index 7411125525d..3c465c876cf 100644 --- a/tests/swoole_http_server/https.phpt +++ b/tests/swoole_http_server/https.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', diff --git a/tests/swoole_http_server/issue_2360.phpt b/tests/swoole_http_server/issue_2360.phpt index 87a644bf1cb..ccd59987849 100644 --- a/tests/swoole_http_server/issue_2360.phpt +++ b/tests/swoole_http_server/issue_2360.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_http_server: issue 2360 (swoole_http_server silently fails to read requests) +swoole_http_server: issue 2360 (Swoole\Http\Server silently fails to read requests) --SKIPIF-- --FILE-- @@ -35,7 +35,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'socket_buffer_size' => SOCKET_BUFFER_SIZE diff --git a/tests/swoole_http_server/json_encode.phpt b/tests/swoole_http_server/json_encode.phpt new file mode 100644 index 00000000000..787ebb024ff --- /dev/null +++ b/tests/swoole_http_server/json_encode.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_http_server: json_encode or serialize Swoole\Http\Request::class OR Swoole\Http\Response::class +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/test") . PHP_EOL; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + Assert::true($request->fd > 0); + Assert::true($response->fd > 0); + + $result = json_decode(json_encode($request), true); + Assert::true($result['fd'] > 0); + + $result = json_decode(json_encode($response), true); + Assert::true($result['fd'] > 0); + + $response->end('OK'); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/json_encode2.phpt b/tests/swoole_http_server/json_encode2.phpt new file mode 100644 index 00000000000..89ca75f6e0b --- /dev/null +++ b/tests/swoole_http_server/json_encode2.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_http_server: json_encode or serialize Swoole\Http\Request::class OR Swoole\Http\Response::class +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/test") . PHP_EOL; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $result = json_decode(json_encode($request), true); + Assert::true($result['fd'] > 0); + + $result = json_decode(json_encode($response), true); + Assert::true($result['fd'] > 0); + + Assert::true($request->fd > 0); + Assert::true($response->fd > 0); + + $response->end('OK'); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/large_url.phpt b/tests/swoole_http_server/large_url.phpt index 3e0c6ccc601..5b42ed818bc 100644 --- a/tests/swoole_http_server/large_url.phpt +++ b/tests/swoole_http_server/large_url.phpt @@ -37,10 +37,10 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header("Content-Type", "text/plain"); $response->end(strlen($request->get['a'])); }); diff --git a/tests/swoole_http_server/max-age.phpt b/tests/swoole_http_server/max-age.phpt new file mode 100644 index 00000000000..610f1faa2da --- /dev/null +++ b/tests/swoole_http_server/max-age.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_http_server: cookies (max-age) +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $uri = "http://127.0.0.1:{$pm->getFreePort()}"; + $cookies = httpRequest($uri)['set_cookie_headers']; + + var_dump(strpos($cookies[0], 'test=123456789') !== false); + var_dump(strpos($cookies[0], 'expires='.date('D, d-M-Y H:i:s \G\M\T', time() + 3600)) !== false); + var_dump(strpos($cookies[0], 'Max-Age=3600') !== false); + var_dump(strpos($cookies[0], 'path=/') !== false); + var_dump(strpos($cookies[0], 'domain=example.com') !== false); + var_dump(strpos($cookies[0], 'secure') !== false); + var_dump(strpos($cookies[0], 'HttpOnly') !== false); + var_dump(strpos($cookies[0], 'SameSite=None') !== false); + var_dump(strpos($cookies[1], 'test=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT') !== false); + var_dump(strpos($cookies[1], 'Max-Age=0') !== false); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->cookie('test', '123456789', time() + 3600, '/', 'example.com', true, true, 'None'); + $response->cookie('test', ''); + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +DONE diff --git a/tests/swoole_http_server/max_concurrency.phpt b/tests/swoole_http_server/max_concurrency.phpt new file mode 100644 index 00000000000..686be09f979 --- /dev/null +++ b/tests/swoole_http_server/max_concurrency.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_http_server: max_concurrency +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/index.html'; + $n = SERVER_MAX_CONCURRENCY; + // 200 + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + System::sleep(0.005); + + // 403, high concurrency + $n = SERVER_MAX_CONCURRENCY; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 503); + }); + } + + // wait + Coroutine::join($cid_list); + + // low concurrency + $n = SERVER_MAX_CONCURRENCY; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + Coroutine::join($cid_list); + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set(['log_file' => '/dev/null', 'max_concurrency' => SERVER_MAX_CONCURRENCY]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/max_input_vars.phpt b/tests/swoole_http_server/max_input_vars.phpt new file mode 100644 index 00000000000..fd5aae34986 --- /dev/null +++ b/tests/swoole_http_server/max_input_vars.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_http_server: max_input_vars +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $maxInputVars = ini_get('max_input_vars') + 10; + $data = []; + $cookies = []; + $temp = 'max_input_vars'; + for ($i = 0; $i < $maxInputVars; $i++) { + $data[$temp . $i] = $temp; + $cookies[] = $temp . $i.'='.$temp; + } + + // post method + httpRequest("http://127.0.0.1:{$pm->getFreePort()}", ['data' => $data, 'headers' => ['Cookie' => implode(';', $cookies)]]); + + // get method + httpRequest("http://127.0.0.1:{$pm->getFreePort()}?".http_build_query($data)); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response){ + $maxInputVars = ini_get('max_input_vars'); + if ($request->get) { + var_dump(count($request->get) == $maxInputVars); + } + + if ($request->post) { + var_dump(count($request->post) == $maxInputVars); + } + + if ($request->cookie) { + var_dump(count($request->cookie) == $maxInputVars); + } + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +%s To increase the limit change max_input_vars in php.ini. +%s To increase the limit change max_input_vars in php.ini. +bool(true) +bool(true) +%s To increase the limit change max_input_vars in php.ini. +bool(true) +DONE diff --git a/tests/swoole_http_server/mixed_server.phpt b/tests/swoole_http_server/mixed_server.phpt index 969a2c09ef5..3e35b986e57 100644 --- a/tests/swoole_http_server/mixed_server.phpt +++ b/tests/swoole_http_server/mixed_server.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm, $tcp_options) { // http2 $http2_client = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(0)); $http2_client->connect(); - $http2_request = new swoole_http2_request; + $http2_request = new Swoole\Http2\Request; $http2_request->method = 'POST'; $http2_request->data = 'Swoole Http2'; $http2_client->send($http2_request); @@ -53,7 +53,7 @@ $pm->parentFunc = function ($pid) use ($pm, $tcp_options) { }; // server side $pm->childFunc = function () use ($pm, $tcp_options) { - $server = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -63,17 +63,17 @@ $pm->childFunc = function () use ($pm, $tcp_options) { $pm->wakeup(); }); // http && http2 - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('Hello ' . $request->rawcontent()); }); // websocket - $server->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { $server->push($frame->fd, 'Hello ' . $frame->data); }); // tcp $tcp_server = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_TCP); $tcp_server->set($tcp_options); - $tcp_server->on('receive', function (swoole_server $server, int $fd, int $reactor_id, string $data) { + $tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) { $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); }); $server->start(); diff --git a/tests/swoole_http_server/new_cookie.phpt b/tests/swoole_http_server/new_cookie.phpt new file mode 100644 index 00000000000..17fc34117ce --- /dev/null +++ b/tests/swoole_http_server/new_cookie.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_http_server: new cookie +--SKIPIF-- + +--FILE-- +withName('test') + ->withValue('123456789') + ->withExpires(time() + 3600) + ->withPath('/path') + ->withDomain('example.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None'); + +var_dump($cookie->toArray()); +$cookie->reset(); +var_dump($cookie->toArray()); +?> +--EXPECTF-- +array(11) { + ["name"]=> + string(4) "test" + ["value"]=> + string(9) "123456789" + ["path"]=> + string(5) "/path" + ["domain"]=> + string(11) "example.com" + ["sameSite"]=> + string(4) "None" + ["priority"]=> + string(0) "" + ["encode"]=> + bool(true) + ["expires"]=> + int(%d) + ["secure"]=> + bool(true) + ["httpOnly"]=> + bool(true) + ["partitioned"]=> + bool(false) +} +array(11) { + ["name"]=> + string(0) "" + ["value"]=> + string(0) "" + ["path"]=> + string(0) "" + ["domain"]=> + string(0) "" + ["sameSite"]=> + string(0) "" + ["priority"]=> + string(0) "" + ["encode"]=> + bool(true) + ["expires"]=> + int(0) + ["secure"]=> + bool(false) + ["httpOnly"]=> + bool(false) + ["partitioned"]=> + bool(false) +} diff --git a/tests/swoole_http_server/no_compression.phpt b/tests/swoole_http_server/no_compression.phpt index fe4ac39123a..9ca4ab7e3de 100644 --- a/tests/swoole_http_server/no_compression.phpt +++ b/tests/swoole_http_server/no_compression.phpt @@ -24,7 +24,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http_server/numeric_header_name.phpt b/tests/swoole_http_server/numeric_header_name.phpt new file mode 100644 index 00000000000..001dc1300dd --- /dev/null +++ b/tests/swoole_http_server/numeric_header_name.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: numeric header name +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->headers['12345'], 'hello'); + Assert::eq($client->headers['12345.678'], 'world'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) { + $response->header(12345, 'hello'); + $response->header(12345.678, 'world'); + $response->end('OK'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/objectCookie.phpt b/tests/swoole_http_server/objectCookie.phpt new file mode 100644 index 00000000000..3626a6e047e --- /dev/null +++ b/tests/swoole_http_server/objectCookie.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_cookie: new cookie +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\Run(function () use ($pm) { + var_dump(httpRequest("http://127.0.0.1:{$pm->getFreePort()}")['set_cookie_headers']); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $cookie = new Swoole\Http\Cookie(); + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + $response->setCookie($cookie); + $cookie->withValue(''); + $response->setCookie($cookie); + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(2) { + [0]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [1]=> + string(62) "key1=deleted; expires=%s; Max-Age=0" +} diff --git a/tests/swoole_http_server/objectCookieMemory.phpt b/tests/swoole_http_server/objectCookieMemory.phpt new file mode 100644 index 00000000000..884306bf050 --- /dev/null +++ b/tests/swoole_http_server/objectCookieMemory.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_http_cookie: new cookie memory +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $previous = memory_get_usage(); + $cookie = new Swoole\Http\Cookie(); + $i = 10000; + while($i--) { + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + } + + global $previous; + global $item; + $current = memory_get_usage(); + $stats = [ + 'id' => $http->getWorkerId(), + 'item' => $item++, + 'prev_mem' => $previous, + 'curr_mem' => $current, + 'diff_mem' => $current - $previous, + ]; + $previous = $current; + + echo json_encode($stats), PHP_EOL; + $response->end('test response'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +{"id":%d,"item":null,"prev_mem":null,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +DONE diff --git a/tests/swoole_http_server/octane_bug_651.phpt b/tests/swoole_http_server/octane_bug_651.phpt new file mode 100644 index 00000000000..5b6af6fe5b4 --- /dev/null +++ b/tests/swoole_http_server/octane_bug_651.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_http_server: Octane bug 651 https://github.com/laravel/octane/issues/651 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->getBody(), 'timeout'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $http->set(['log_file' => '/dev/null']); + + $timerTable = new Swoole\Table(250); + $timerTable->column('worker_pid', Swoole\Table::TYPE_INT); + $timerTable->column('time', Swoole\Table::TYPE_INT); + $timerTable->column('fd', Swoole\Table::TYPE_INT); + $timerTable->create(); + + $http->on("WorkerStart", function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + + $http->on('start', function ($server) use ($timerTable) { + Swoole\Timer::tick(500, function ($id) use ($timerTable, $server) { + foreach ($timerTable as $workerId => $row) { + if ((time() - $row['time']) > 3) { + $timerTable->del($workerId); + $newRes = Swoole\Http\Response::create($server, $row['fd']);; + if ($newRes) { + Swoole\Timer::clear($id); + $newRes->status(408); + $newRes->end('timeout'); + Swoole\Process::kill($row['worker_pid'], 9); + return; + } + } + } + }); + }); + + $http->on('Request', function ($request, $response) use ($http, $timerTable) { + $timerTable->set($http->getWorkerId(), [ + 'worker_pid' => $http->getWorkerPid(), + 'time' => time(), + 'fd' => $request->fd, + ]); + sleep(10); + $response->end('Hello'); + $timerTable->del($http->getWorkerId()); + }); + + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_http_server/pipeline.phpt b/tests/swoole_http_server/pipeline.phpt index a67647c3e3f..c3d25bb4c79 100644 --- a/tests/swoole_http_server/pipeline.phpt +++ b/tests/swoole_http_server/pipeline.phpt @@ -12,7 +12,7 @@ $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) { exit("connect failed. Error: {$client->errCode}\n"); } @@ -51,13 +51,13 @@ HTTP; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("WorkerStart", function ($serv, $wid) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(var_dump_return($request->get, $request->server)); }); $http->start(); diff --git a/tests/swoole_http_server/post.phpt b/tests/swoole_http_server/post.phpt index cf36ffb8194..1333bee839c 100644 --- a/tests/swoole_http_server/post.phpt +++ b/tests/swoole_http_server/post.phpt @@ -29,8 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { $pm->childFunc = function () use ($pm, $html) { $mode = SERVER_MODE_RANDOM; - $mode = SWOOLE_BASE; - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), $mode); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); $serv->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/purge_method.phpt b/tests/swoole_http_server/purge_method.phpt index 7edbb905eb1..ff09ff8be4c 100644 --- a/tests/swoole_http_server/purge_method.phpt +++ b/tests/swoole_http_server/purge_method.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/range.phpt b/tests/swoole_http_server/range.phpt new file mode 100644 index 00000000000..5891fc03c46 --- /dev/null +++ b/tests/swoole_http_server/range.phpt @@ -0,0 +1,158 @@ +--TEST-- +swoole_http_server: range +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + // range + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + Assert::same('bytes 0-15/218787', $response['headers']['content-range']); + $lastModified = $response['headers']['last-modified'] ?? null; + Assert::notNull($lastModified); + Assert::null($response['headers']['accept-ranges'] ?? null); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 0-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + // exit; + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=16-31']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 16-31/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 16, 16))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-16']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 218771-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, -16))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=128-']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 128-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 128))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-0,-1']]); + Assert::same($response['statusCode'], 206); + Assert::isEmpty($response['headers']['content-range'] ?? null); + Assert::notEq(preg_match('/multipart\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false); + $boundary = $matches[1]; + $expect = sprintf(<<getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15,32-63']]); + Assert::same($response['statusCode'], 206); + Assert::notEq(preg_match('/multipart\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false); + $boundary = $matches[1]; + $expect = sprintf(<<getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => $lastModified]]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => 'test']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + + $lastModifiedTime = strtotime($lastModified); + $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)]]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + + $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)]]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + + // head + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'method' => 'HEAD']); + Assert::same($response['statusCode'], 200); + Assert::isEmpty($response['body']); + Assert::same($response['headers']['accept-ranges'], 'bytes'); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'method' => 'HEAD', 'headers' => ['Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 0-15/218787', $response['headers']['content-range']); + Assert::isEmpty($response['body']); + Assert::null($response['headers']['accept-ranges'] ?? null); + + // data boundary + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'abc']]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=abc']]); + Assert::same($response['statusCode'], 416); + Assert::isEmpty($response['body']); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-999999']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=999999']]); + Assert::same($response['statusCode'], 416); + Assert::isEmpty($response['body']); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/range2.phpt b/tests/swoole_http_server/range2.phpt new file mode 100644 index 00000000000..37cbfe90d69 --- /dev/null +++ b/tests/swoole_http_server/range2.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http_server: range - confusing header +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + // range + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['-Range' => 'none', 'Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/rawContent.phpt b/tests/swoole_http_server/rawContent.phpt index 44d6f62a280..7d4188d8503 100644 --- a/tests/swoole_http_server/rawContent.phpt +++ b/tests/swoole_http_server/rawContent.phpt @@ -35,12 +35,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { if ($request->server['request_uri'] === '/rawContent') { $response->end($request->rawContent()); } else { diff --git a/tests/swoole_http_server/rawCookie.phpt b/tests/swoole_http_server/rawCookie.phpt index e41c0c01127..59a0e2e33b9 100644 --- a/tests/swoole_http_server/rawCookie.phpt +++ b/tests/swoole_http_server/rawCookie.phpt @@ -24,15 +24,15 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($httpClient->body, "Hello World!"); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $simple_http_server) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $name = "name"; $value = "value"; // $expire = $request->swoole_server["request_time"] + 3600; @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm, $simple_http_server) { $httpOnly = true; // string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false ]]]]]] $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); - $expect = "name=value; path=/; httponly"; + $expect = "name=value; path=/; HttpOnly"; Assert::assert(in_array($expect, $response->cookie, true)); $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); $response->rawcookie("rawcontent", $request->rawcontent()); diff --git a/tests/swoole_http_server/redirect.phpt b/tests/swoole_http_server/redirect.phpt index 366e99a99e5..7227cb0704f 100644 --- a/tests/swoole_http_server/redirect.phpt +++ b/tests/swoole_http_server/redirect.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { if ($request->server['path_info'] == '/nonexistent') { $response->redirect('/test.jpg'); } diff --git a/tests/swoole_http_server/reset_concurrency_with_base.phpt b/tests/swoole_http_server/reset_concurrency_with_base.phpt new file mode 100644 index 00000000000..1c3b12016a6 --- /dev/null +++ b/tests/swoole_http_server/reset_concurrency_with_base.phpt @@ -0,0 +1,90 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_BASE] +--SKIPIF-- + +--FILE-- +column('pid', Table::TYPE_INT); +$table->create(); + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm) { + run(function () use ($pm) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::join($coroutines); + Co::sleep(0.1); + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + $pm->kill(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm, $counter, $table) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'worker_num' => 4, + 'max_concurrency' => 160, + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function ($server, $wid) use ($pm, $table) { + if ($wid === 0) { + $pm->wakeup(); + } + $pid = posix_getpid(); + $table->set('worker_' . $wid, ['pid' => $pid]); + // echo "Worker #{$wid}(pid=$pid) is started\n"; + }); + $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) { + $c = $counter->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], N); + $pid = posix_getpid(); + foreach ($table as $val) { + if ($val['pid'] !== $pid) { + posix_kill($val['pid'], SIGKILL); + } + } + posix_kill($pid, SIGKILL); + } else { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], 1); + $response->end(json_encode($stats)); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/reset_concurrency_with_process.phpt b/tests/swoole_http_server/reset_concurrency_with_process.phpt new file mode 100644 index 00000000000..97cbf5432ac --- /dev/null +++ b/tests/swoole_http_server/reset_concurrency_with_process.phpt @@ -0,0 +1,97 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_PROCESS] +--SKIPIF-- + +--FILE-- +column('pid', Table::TYPE_INT); +$table->create(); + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm) { + run(function () use ($pm) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::sleep(0.1); + $pm->wait(); + + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + + /** + * PROCESS 模式下 Worker 进程退出时连接不会被关闭,这与 BASE 模式不同,因此需要先关闭服务器,其他正在运行的协程才会获得返回值 + */ + $pm->kill(); + + Co::join($coroutines); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm, $counter, $table) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 4, + 'max_concurrency' => 160, + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function ($server, $wid) use ($pm, $table) { + if ($wid === 0) { + $pm->wakeup(); + } + $pid = posix_getpid(); + $table->set('worker_' . $wid, ['pid' => $pid]); + // echo "Worker #{$wid}(pid=$pid) is started\n"; + }); + $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) { + $c = $counter->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], N); + $pid = posix_getpid(); + foreach ($table as $val) { + if ($val['pid'] !== $pid) { + posix_kill($val['pid'], SIGKILL); + } + } + posix_kill($pid, SIGKILL); + } else { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], 1); + $response->end(json_encode($stats)); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/response_create.phpt b/tests/swoole_http_server/response_create.phpt index 0db1ef1acaf..cfea9431ae5 100644 --- a/tests/swoole_http_server/response_create.phpt +++ b/tests/swoole_http_server/response_create.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server("127.0.0.1", $pm->getFreePort()); + $serv = new Swoole\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([Constant::OPTION_LOG_FILE => '/dev/null']); $serv->on( Constant::EVENT_WORKER_START, diff --git a/tests/swoole_http_server/send_500_to_client.phpt b/tests/swoole_http_server/send_500_to_client.phpt new file mode 100644 index 00000000000..bfdb7b95c9f --- /dev/null +++ b/tests/swoole_http_server/send_500_to_client.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_http_server: When the process restarts, send a 500 status code to the clients waiting in the queue +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function() use ($pm) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $pm) { + $waitGroup->add(); + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 15]); + $client->get('/'); + Assert::true($client->statusCode == 200); + $waitGroup->done(); + }); + + sleep(1); + + for ($i = 0; $i < 10; $i++) { + go(function() use ($waitGroup, $pm) { + $waitGroup->add(); + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 15]); + $client->get('/'); + Assert::true($client->statusCode == 503); + $waitGroup->done(); + }); + } + + $waitGroup->wait(); + $pm->kill(); + }); + echo 'DONE'; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'enable_coroutine' => true, + 'worker_max_concurrency' => 1, + 'max_wait_time' => 10, + 'reload_async' => true, + 'hook_flags' => SWOOLE_HOOK_ALL + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Request $request, Response $response) use ($http) { + $http->reload(); + sleep(3); + $response->end(); + }); + + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile.phpt b/tests/swoole_http_server/sendfile.phpt index 7e35fb86b31..9e6123e1a5f 100644 --- a/tests/swoole_http_server/sendfile.phpt +++ b/tests/swoole_http_server/sendfile.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -26,7 +26,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Content-Type', 'application/octet-stream'); $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); $response->sendfile('/tmp/sendfile.txt'); diff --git a/tests/swoole_http_server/sendfile_client_reset.phpt b/tests/swoole_http_server/sendfile_client_reset.phpt new file mode 100644 index 00000000000..aea7b0f1466 --- /dev/null +++ b/tests/swoole_http_server/sendfile_client_reset.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_http_server: client reset when sending file +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Co\run(function () use ($pm) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(['socket_buffer_size' => 128 * 1024]); + Assert::true($client->connect('127.0.0.1', $pm->getFreePort())); + $client->send("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"); + $resp = ''; + + Co\go(function () use ($pm) { + $file = file_get_contents('http://127.0.0.1:' . $pm->getFreePort()); + Assert::eq(md5_file(TMP_FILE), md5($file)); + }); + + while (true) { + $data = $client->recv(); + System::sleep(0.01); + Assert::notEmpty($data); + $resp .= $data; + if (strlen($resp) > 2 * 1024 * 1024) { + $client->close(); + break; + } + } + }); + + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $response->header('Content-Type', 'application/octet-stream'); + $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); + $response->sendfile(TMP_FILE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(TMP_FILE); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_dir.phpt b/tests/swoole_http_server/sendfile_dir.phpt new file mode 100644 index 00000000000..5d95d09f0fa --- /dev/null +++ b/tests/swoole_http_server/sendfile_dir.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_http_server: sendfile dir +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $recv_file = @file_get_contents("http://127.0.0.1:{$pm->getFreePort()}"); + Assert::eq($recv_file, false); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function ($request, $response) { + $filename = '/tmp'; + $response->header('Content-Type', 'application/octet-stream', true); + Assert::eq(@$response->sendfile($filename), false); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_link.phpt b/tests/swoole_http_server/sendfile_link.phpt new file mode 100644 index 00000000000..64163696a0c --- /dev/null +++ b/tests/swoole_http_server/sendfile_link.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_http_server: sendfile link +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $send_file) { + $recv_file = @file_get_contents("http://127.0.0.1:{$pm->getFreePort()}"); + Assert::eq($recv_file, $send_file); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function ($request, $response) { + $response->header('Content-Type', 'application/octet-stream', true); + Assert::eq($response->sendfile(LINK), true); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LINK); +unlink(FILE); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_no_keepalive.phpt b/tests/swoole_http_server/sendfile_no_keepalive.phpt index 3886d0a4437..c8cb1556108 100644 --- a/tests/swoole_http_server/sendfile_no_keepalive.phpt +++ b/tests/swoole_http_server/sendfile_no_keepalive.phpt @@ -41,7 +41,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(__FILE__); }); diff --git a/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt index a7ac241ef54..44d76ed4fc6 100644 --- a/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt +++ b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(__FILE__); }); diff --git a/tests/swoole_http_server/sendfile_with_ssl.phpt b/tests/swoole_http_server/sendfile_with_ssl.phpt index 0ce6ade1c08..95cbb374650 100644 --- a/tests/swoole_http_server/sendfile_with_ssl.phpt +++ b/tests/swoole_http_server/sendfile_with_ssl.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Content-Type', 'application/octet-stream'); $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); $response->sendfile('/tmp/sendfile.txt'); diff --git a/tests/swoole_http_server/slow_client.phpt b/tests/swoole_http_server/slow_client.phpt new file mode 100644 index 00000000000..a12380ac5fc --- /dev/null +++ b/tests/swoole_http_server/slow_client.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_http_server: slow client +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.01", $pm->getFreePort()); + $html = base64_encode(random_bytes(rand(1024, 65536))); + $len = strlen($html); + $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"; + $chunks = str_split($data, rand(5, 255)); + foreach ($chunks as $out) { + $client->send($out); + usleep(100); + } + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) +{ + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'text/html'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + $response->end("OK"); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/slow_large_post.phpt b/tests/swoole_http_server/slow_large_post.phpt new file mode 100644 index 00000000000..11634bf9f5c --- /dev/null +++ b/tests/swoole_http_server/slow_large_post.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_http_server: slow large post +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.0.1", $pm->getFreePort()); + $post_data = KEY . '=' . urlencode(VALUE); + $len = strlen($post_data); + $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"; + + $client->send(substr($data, 0, N1)); + usleep(30000); + $client->send(substr($data, N1, N2)); + usleep(30000); + $client->send(substr($data, N1 + N2)); + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + Assert::eq(VALUE, $request->post[KEY]); + $response->end("OK"); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt b/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt new file mode 100644 index 00000000000..4c6d2616c49 --- /dev/null +++ b/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http_server/static_handler: mimetype not exists +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + Assert::eq(get("http://127.0.0.1:{$pm->getFreePort()}/examples/not_exists.xyz")->getStatusCode(), 404); + Assert::eq(get("http://127.0.0.1:{$pm->getFreePort()}/not_exists.xyz")->getStatusCode(), 500); + + $resp = get("http://127.0.0.1:{$pm->getFreePort()}/examples/exists.xyz"); + Assert::eq($resp->getBody(), TEST_RANDOM_BYTES); + Assert::eq($resp->getHeaders()['content-type'], 'application/octet-stream'); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => TEST_DOCUMENT_ROOT, + 'static_handler_locations' => ['/examples'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +//unlink(TEST_FILE); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/static_handler/read_link_2.phpt b/tests/swoole_http_server/static_handler/read_link_2.phpt new file mode 100644 index 00000000000..6ce49e38661 --- /dev/null +++ b/tests/swoole_http_server/static_handler/read_link_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_server/static_handler: link to a file outside the document root +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $doc_root, $image_dir, $image_link) { + Swoole\Coroutine\run(function () use ($pm, $doc_root, $image_dir, $image_link) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/{$image_dir}/image.jpg"); + Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $doc_root, $image_dir, $image_link) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => $doc_root, + 'static_handler_locations' => ['/image'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +$cleanup_fn(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/static_handler/read_link_file.phpt b/tests/swoole_http_server/static_handler/read_link_file.phpt index 14046892a45..f7bc7b2893d 100644 --- a/tests/swoole_http_server/static_handler/read_link_file.phpt +++ b/tests/swoole_http_server/static_handler/read_link_file.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { 'log_file' => '/dev/null', 'open_http2_protocol' => true, 'enable_static_handler' => true, - 'document_root' => dirname(dirname(dirname(__DIR__))) . '/', + 'document_root' => dirname(__DIR__, 3) . '/', 'static_handler_locations' => ['/examples'] ]); $http->on('workerStart', function () use ($pm) { diff --git a/tests/swoole_http_server/static_handler/relative_path_2.phpt b/tests/swoole_http_server/static_handler/relative_path_2.phpt new file mode 100644 index 00000000000..26c06d14c21 --- /dev/null +++ b/tests/swoole_http_server/static_handler/relative_path_2.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server/static_handler: static handler with relative path [2] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../examples/test.jpg", ['http2' => $http2]); + Assert::notEmpty($data); + Assert::same(md5($data), md5_file(TEST_IMAGE)); + + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docs/swoole-logo.svg", ['http2' => $http2]); + Assert::eq("hello world", $data); + }); + } + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => SOURCE_ROOT_PATH . '/examples', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end("hello world"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/static_handler/relative_path_3.phpt b/tests/swoole_http_server/static_handler/relative_path_3.phpt new file mode 100644 index 00000000000..a174c88cd40 --- /dev/null +++ b/tests/swoole_http_server/static_handler/relative_path_3.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_http_server/static_handler: doc root with same prefix +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $doc1_root, $doc2_root) { + Swoole\Coroutine\run(function () use ($pm, $doc1_root, $doc2_root) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docroot/image.jpg"); + Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docroot2/uuid.txt"); + Assert::isEmpty($data); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $doc1_root, $doc2_root) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => $doc1_root, + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +$cleanup_fn(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/task/enable_coroutine.phpt b/tests/swoole_http_server/task/enable_coroutine.phpt index 79cdc8ed900..5d0ebed260d 100644 --- a/tests/swoole_http_server/task/enable_coroutine.phpt +++ b/tests/swoole_http_server/task/enable_coroutine.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { global $randoms; $n = $request->get['n']; switch ($request->server['path_info']) { @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->get("/random?n={$task->data}"); $task->finish([$task->data, $cli->body]); diff --git a/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt b/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt index 5dff866c705..266a44e8bea 100644 --- a/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt +++ b/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt @@ -11,22 +11,22 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, 'task_enable_coroutine' => true ]); - $server->on('workerStart', function (swoole_http_server $server, int $wid) use ($pm) { + $server->on('workerStart', function (Swoole\Http\Server $server, int $wid) use ($pm) { if ($wid === 0) { $server->taskCo(['foo'], 1); } }); - $server->on('workerError', function (swoole_http_server $server) use ($pm) { + $server->on('workerError', function (Swoole\Http\Server $server) use ($pm) { $pm->wakeup(); }); $server->on('request', function () { }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $server->finish('bar'); }); $server->start(); diff --git a/tests/swoole_http_server/task/use_object.phpt b/tests/swoole_http_server/task/use_object.phpt index e2ad8f69276..6a64fee6081 100644 --- a/tests/swoole_http_server/task/use_object.phpt +++ b/tests/swoole_http_server/task/use_object.phpt @@ -6,7 +6,7 @@ require __DIR__ . '/../../include/skipif.inc'; ?> --FILE-- set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, diff --git a/tests/swoole_http_server/tmp-content-type.phpt b/tests/swoole_http_server/tmp-content-type.phpt new file mode 100644 index 00000000000..ed8631d05b7 --- /dev/null +++ b/tests/swoole_http_server/tmp-content-type.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_http_server: tmp content-type +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new GuzzleHttpClient(); + $baseUrl = 'http://127.0.0.1:' . $pm->getFreePort(); + $res = $client->post($baseUrl . '/', [ + 'multipart' => [ + [ + 'name' => 'file', + 'contents' => fopen(__FILE__, 'r'), + 'filename' => basename(__FILE__), + 'headers' => ['Content-Type' => 'application/php-script'] + ], + ], + ]); + + $status = $res->getStatusCode(); + $body = $res->getBody()->getContents(); + Assert::eq($status, 200); + $result = json_decode($body, true); + Assert::eq($result['file']['name'], basename(__FILE__)); + Assert::eq($result['file']['type'], 'application/php-script'); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end(json_encode($request->files)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt b/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt index 555112012ca..1f2613472de 100644 --- a/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt +++ b/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { static $pre_cookie; diff --git a/tests/swoole_http_server/trailer.phpt b/tests/swoole_http_server/trailer.phpt index b3f315a9aaf..fa6710ccb12 100644 --- a/tests/swoole_http_server/trailer.phpt +++ b/tests/swoole_http_server/trailer.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, @@ -30,9 +30,9 @@ $pm->childFunc = function () use ($pm) { $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('trailer', 'Content-MD5'); $data = 'hello world'; - $response->write($data); - $response->trailer('Content-MD5', md5($data)); - $response->end(); + Assert::true($response->write($data)); + Assert::true($response->trailer('Content-MD5', md5($data))); + Assert::true($response->end()); }); $http->start(); }; diff --git a/tests/swoole_http_server/unixsocket.phpt b/tests/swoole_http_server/unixsocket.phpt index 46a7a808c16..20571375a87 100644 --- a/tests/swoole_http_server/unixsocket.phpt +++ b/tests/swoole_http_server/unixsocket.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; diff --git a/tests/swoole_http_server/unset_response_header.phpt b/tests/swoole_http_server/unset_response_header.phpt index 839347e7e50..5cfb82fa073 100644 --- a/tests/swoole_http_server/unset_response_header.phpt +++ b/tests/swoole_http_server/unset_response_header.phpt @@ -19,12 +19,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("workerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Foo', 'Bar'); $response->status(500); try { diff --git a/tests/swoole_http_server/upload.phpt b/tests/swoole_http_server/upload.phpt index d88350104bd..1865d51d5e9 100644 --- a/tests/swoole_http_server/upload.phpt +++ b/tests/swoole_http_server/upload.phpt @@ -21,13 +21,10 @@ $pm->parentFunc = function () use ($pm) { $post_data = array('test' => str_repeat('a', 80)); - if (function_exists("curl_file_create")) - { + if (function_exists("curl_file_create")) { $cfile = curl_file_create($file); $post_data['file'] = $cfile; - } - else - { + } else { $post_data['file'] = '@' . $file; } @@ -42,17 +39,17 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', ]); $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(md5_file($request->files['file']['tmp_name'])); }); diff --git a/tests/swoole_http_server/upload4.phpt b/tests/swoole_http_server/upload4.phpt index b06e84b0508..fc9e50501b9 100644 --- a/tests/swoole_http_server/upload4.phpt +++ b/tests/swoole_http_server/upload4.phpt @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end($request->files['file']['name']); }); diff --git a/tests/swoole_http_server/uploadFile.phpt b/tests/swoole_http_server/uploadFile.phpt index f3d1cb200c4..8c6015c15a3 100644 --- a/tests/swoole_http_server/uploadFile.phpt +++ b/tests/swoole_http_server/uploadFile.phpt @@ -34,12 +34,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { if ( empty($request->files['upfile']) || md5_file(TEST_IMAGE) !== md5_file($request->files['upfile']['tmp_name']) || diff --git a/tests/swoole_http_server/upload_02.phpt b/tests/swoole_http_server/upload_02.phpt index 889a427f2fc..c7bd148bc1d 100644 --- a/tests/swoole_http_server/upload_02.phpt +++ b/tests/swoole_http_server/upload_02.phpt @@ -64,12 +64,12 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->files + $request->post)); }); $http->start(); diff --git a/tests/swoole_http_server/upload_03.phpt b/tests/swoole_http_server/upload_03.phpt index d3ddae0e721..c820536eb92 100644 --- a/tests/swoole_http_server/upload_03.phpt +++ b/tests/swoole_http_server/upload_03.phpt @@ -32,11 +32,11 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::assert(is_array($json)); Assert::true(isset($json['folder_id'])); Assert::true(isset($json['name'])); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); @@ -45,7 +45,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->post)); }); diff --git a/tests/swoole_http_server/upload_big_file.phpt b/tests/swoole_http_server/upload_big_file.phpt new file mode 100644 index 00000000000..04c9e43ed75 --- /dev/null +++ b/tests/swoole_http_server/upload_big_file.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_server: upload big file +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + + $file = TEST_IMAGE; + + $post_data = array( + 'test' => str_repeat('a', 80), + 'hello' => base64_encode(random_bytes(rand(10, 128))), + 'world' => base64_encode(random_bytes(rand(1024, 8192))), + ); + + if (function_exists("curl_file_create")) { + $cfile = curl_file_create($file); + $post_data['file'] = $cfile; + } else { + $post_data['file'] = '@' . $file; + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //POST数据 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_setopt($ch, CURLOPT_TIMEOUT, 1000); + + $res = curl_exec($ch); + Assert::assert(!empty($res)); + Assert::same($res, md5_file($file)); + curl_close($ch); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'package_max_length' => 64 * 1024, + 'upload_max_filesize' => 8 * 1024 * 1024, + ]); + + $http->on("WorkerStart", function () use ($pm) { + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_server/upload_file_array_default.phpt b/tests/swoole_http_server/upload_file_array_default.phpt index ac17270b082..a21b6baf960 100644 --- a/tests/swoole_http_server/upload_file_array_default.phpt +++ b/tests/swoole_http_server/upload_file_array_default.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), 'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'), ]; - + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); curl_setopt($ch, CURLOPT_HEADER, 0); @@ -29,19 +29,24 @@ $pm->parentFunc = function () use ($pm) { $result = curl_exec($ch); curl_close($ch); - echo "$result\n"; + $json = json_decode($result, true); + + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0); + assert_upload_file($json['form']['file'], '/tmp/swoole.upfile.fixture2', 'photo.jpg', 'image/jpeg', filesize(TEST_IMAGE), 0); + assert_upload_file($json['form']['group']['file'], '/tmp/swoole.upfile.fixture3', 'swoole-logo.svg', 'image/svg+xml', filesize(TEST_IMAGE2), 0); + $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', ]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (!is_array($files) || empty($files['file']['tmp_name']) @@ -54,7 +59,7 @@ $pm->childFunc = function () use ($pm) { $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; $files['form']['file']['tmp_name'] = '/tmp/swoole.upfile.fixture2'; $files['form']['group']['file']['tmp_name'] = '/tmp/swoole.upfile.fixture3'; - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -63,35 +68,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file' => - array ( - 'name' => 'image.jpg', - 'type' => 'application/octet-stream', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 218787, - ), - 'form' => - array ( - 'file' => - array ( - 'name' => 'photo.jpg', - 'type' => 'image/jpeg', - 'tmp_name' => '/tmp/swoole.upfile.fixture2', - 'error' => 0, - 'size' => 218787, - ), - 'group' => - array ( - 'file' => - array ( - 'name' => 'swoole-logo.svg', - 'type' => 'image/svg+xml', - 'tmp_name' => '/tmp/swoole.upfile.fixture3', - 'error' => 0, - 'size' => 7424, - ), - ), - ), -) diff --git a/tests/swoole_http_server/upload_file_array_parsed.phpt b/tests/swoole_http_server/upload_file_array_parsed.phpt index ca3cba3f7f5..cb2f15fea74 100644 --- a/tests/swoole_http_server/upload_file_array_parsed.phpt +++ b/tests/swoole_http_server/upload_file_array_parsed.phpt @@ -10,15 +10,15 @@ skip_if_function_not_exist('curl_file_create'); parentFunc = function () use ($pm) { $formData = [ - 'file' => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'), - 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), + 'file' => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'), + 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), 'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'), ]; - + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); curl_setopt($ch, CURLOPT_HEADER, 0); @@ -29,12 +29,40 @@ $pm->parentFunc = function () use ($pm) { $result = curl_exec($ch); curl_close($ch); - echo "$result\n"; + $json = json_decode($result, true); + + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0); + assert_upload_file($json['form'], [ + 'file' => '/tmp/swoole.upfile.fixture2', + 'group' => [ + 'file' => '/tmp/swoole.upfile.fixture3', + ], + ], [ + 'file' => 'photo.jpg', + 'group' => [ + 'file' => 'swoole-logo.svg', + ], + ], [ + 'file' => 'image/jpeg', + 'group' => [ + 'file' => 'image/svg+xml', + ], + ], [ + 'file' => filesize(TEST_IMAGE), + 'group' => [ + 'file' => filesize(TEST_IMAGE2), + ], + ], [ + 'file' => 0, + 'group' => [ + 'file' => 0, + ], + ]); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', 'http_parse_files' => true, @@ -42,7 +70,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (!is_array($files) || empty($files['file']['tmp_name']) @@ -52,10 +80,10 @@ $pm->childFunc = function () use ($pm) { $response->end(); return; } - $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; - $files['form']['tmp_name']['file'] = '/tmp/swoole.upfile.fixture2'; + $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; + $files['form']['tmp_name']['file'] = '/tmp/swoole.upfile.fixture2'; $files['form']['tmp_name']['group']['file'] = '/tmp/swoole.upfile.fixture3'; - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -64,56 +92,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file' => - array ( - 'name' => 'image.jpg', - 'type' => 'application/octet-stream', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 218787, - ), - 'form' => - array ( - 'name' => - array ( - 'file' => 'photo.jpg', - 'group' => - array ( - 'file' => 'swoole-logo.svg', - ), - ), - 'type' => - array ( - 'file' => 'image/jpeg', - 'group' => - array ( - 'file' => 'image/svg+xml', - ), - ), - 'tmp_name' => - array ( - 'file' => '/tmp/swoole.upfile.fixture2', - 'group' => - array ( - 'file' => '/tmp/swoole.upfile.fixture3', - ), - ), - 'error' => - array ( - 'file' => 0, - 'group' => - array ( - 'file' => 0, - ), - ), - 'size' => - array ( - 'file' => 218787, - 'group' => - array ( - 'file' => 7424, - ), - ), - ), -) diff --git a/tests/swoole_http_server/upload_file_empty.phpt b/tests/swoole_http_server/upload_file_empty.phpt index 7a9a5dafec8..36281608ecb 100644 --- a/tests/swoole_http_server/upload_file_empty.phpt +++ b/tests/swoole_http_server/upload_file_empty.phpt @@ -37,26 +37,32 @@ $pm->parentFunc = function () use ($pm) { stream_set_chunk_size($sock, 2 * 1024 * 1024); $response = fread($sock, 2 * 1024 * 1024); fclose($sock); + [$header, $body] = explode("\r\n\r\n", $response); + $json = json_decode($body, true); + Assert::true(is_array($json)); + Assert::true(isset($json['file1'])); + assert_upload_file($json['file1'], '/tmp/swoole.upfile.fixture1', 'empty.txt', 'text/plain', 0, 0); + + Assert::true(isset($json['file2'])); + assert_upload_file($json['file2'], '', '', '', 0, 4); - $result = ltrim(strstr($response, "\r\n\r\n")); - echo "$result\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null' ]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (isset($files['file1']['tmp_name'])) { $files['file1']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; } - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -65,21 +71,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file1' => - array ( - 'name' => 'empty.txt', - 'type' => 'text/plain', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 0, - ), - 'file2' => - array ( - 'name' => '', - 'type' => '', - 'tmp_name' => '', - 'error' => 4, - 'size' => 0, - ), -) diff --git a/tests/swoole_http_server/upload_max_filesize.phpt b/tests/swoole_http_server/upload_max_filesize.phpt new file mode 100644 index 00000000000..df73887dc2e --- /dev/null +++ b/tests/swoole_http_server/upload_max_filesize.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_server: upload max filesize +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + + $file = TEST_IMAGE; + + $post_data = array( + 'test' => str_repeat('a', 80), + 'hello' => base64_encode(random_bytes(rand(10, 128))), + 'world' => base64_encode(random_bytes(rand(1024, 8192))), + ); + + if (function_exists("curl_file_create")) { + $cfile = curl_file_create($file); + $post_data['file'] = $cfile; + } else { + $post_data['file'] = '@' . $file; + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //POST数据 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_setopt($ch, CURLOPT_TIMEOUT, 1000); + + $res = curl_exec($ch); + Assert::isEmpty(($res)); + Assert::eq(curl_getinfo($ch)['http_code'], 413); + curl_close($ch); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + + $http->set([ + 'log_file' => '/dev/null', + 'package_max_length' => 64 * 1024, + 'upload_max_filesize' => 128 * 1024, + ]); + + $http->on("WorkerStart", function () use ($pm) { + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_server/worker_max_concurrency.phpt b/tests/swoole_http_server/worker_max_concurrency.phpt new file mode 100644 index 00000000000..027628beb8b --- /dev/null +++ b/tests/swoole_http_server/worker_max_concurrency.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_http_server: worker_max_concurrency +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/'; + $n = SERVER_WORKER_MAX_CONCURRENCY * 3; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + // wait + Coroutine::join($cid_list); + $c = Swoole\Coroutine\Http\get($url.'stats'); + Assert::eq($c->getStatusCode(), 200); + $json = json_decode($c->getBody()); + Assert::true(isset($json->coroutine_peek_num)); + Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY); + + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { +// $mode = SWOOLE_BASE; + $mode = SERVER_MODE_RANDOM; + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $http) { + if ($request->server['request_uri'] == '/stats') { + $response->end(json_encode($http->stats())); + return; + } + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/zstd.phpt b/tests/swoole_http_server/zstd.phpt new file mode 100644 index 00000000000..79d9d1753c7 --- /dev/null +++ b/tests/swoole_http_server/zstd.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_server: support zstd compress +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $data) { + run(function () use ($pm, $data) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->setHeaders(['Accept-Encoding' => 'zstd']); + $client->get('/'); + Assert::true($client->body == $data); + Assert::true($client->headers['content-encoding'] == 'zstd'); + Assert::true($client->headers['content-length'] != strlen($client->body)); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $data) { + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'compression_level' => 20 + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($data) { + $resp->end($data); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/bug_4519.phpt b/tests/swoole_http_server_coro/bug_4519.phpt new file mode 100644 index 00000000000..6810478c8dc --- /dev/null +++ b/tests/swoole_http_server_coro/bug_4519.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_http_server_coro: bug #4519 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$port = $pm->getFreePort(); +$data = str_repeat('你好你好你好', 10000); +$length = strlen($data); + +$pm->parentFunc = function ($pid) use ($pm, $data, $port) { + run(function () use ($pm, $data, $port) { + $client = new Client('127.0.0.1', $port); + $client->setHeaders([ + 'Content-type' => 'application/x-www-form-urlencoded', + ]); + $client->post('/api', ['test' => $data]); + $client->close(); + $pm->kill(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm, $length, $port) { + run(function () use ($pm, $length, $port) { + $server = new Server('127.0.0.1', $port, false); + $server->handle('/api', function (Request $request, Response $response) use ($length){ + Assert::assert(sizeof($request->post) == 1 && strlen($request->post['test']) == $length); + }); + Swoole\Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/check_cookie_crlf.phpt b/tests/swoole_http_server_coro/check_cookie_crlf.phpt index c114a4632ec..5a6a7d26404 100644 --- a/tests/swoole_http_server_coro/check_cookie_crlf.phpt +++ b/tests/swoole_http_server_coro/check_cookie_crlf.phpt @@ -54,5 +54,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Warning: Swoole\Http\Response::rawcookie(): Header may not contain more than a single header, new line detected in %s +Warning: Swoole\Http\Response::rawcookie(): The value cannot contain ",", ";", " ", "\t", "\r", "\n", "\013", or "\014" in %s DONE diff --git a/tests/swoole_http_server_coro/chunked_pipeline_request.phpt b/tests/swoole_http_server_coro/chunked_pipeline_request.phpt new file mode 100644 index 00000000000..7a89adbf200 --- /dev/null +++ b/tests/swoole_http_server_coro/chunked_pipeline_request.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_http_server_coro: chunked and pipeline request +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + chunked_request($pm); +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end($request->rawContent() . EOF); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_http_server_coro/close_socket.phpt b/tests/swoole_http_server_coro/close_socket.phpt new file mode 100644 index 00000000000..a7448f8d2f0 --- /dev/null +++ b/tests/swoole_http_server_coro/close_socket.phpt @@ -0,0 +1,108 @@ +--TEST-- +swoole_http_server_coro: close socket +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + for ($i = 0; $i < 2; $i++) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->get('/')); + Assert::assert(str_contains($cli->headers['server'], 'BWS') or str_contains($cli->headers['server'], 'bfe')); + } + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function (Request $request, Response $response) { + $response->detach(); + $socket = $response->socket; + $headers = []; + $checkHttpCode = false; + $isHeaderSended = false; + $httpCode = 200; + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, 'https://www.baidu.com/'); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$headers, &$httpCode, &$checkHttpCode) { + if (!$checkHttpCode) { + $checkHttpCode = true; + preg_match('/HTTP\/[0-9.]+\s(\d+)\s(.*)/', $header, $matches, PREG_OFFSET_CAPTURE, 0); + if (!empty($matches)) { + $httpCode = $matches[1][0]; + return strlen($header); + } + } + $content = trim($header); + + if (empty($content)) return strlen($header); + list($key, $value) = explode(": ", $content); + if (in_array(strtolower($key), ['content-length', 'transfer-encoding'])) return strlen($header); + $headers[$key] = $value; + return strlen($header); + }); + + curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($curl, $str) use ($response, &$socket, &$headers, &$isHeaderSended, &$httpCode) { + if (!$isHeaderSended) { + $isHeaderSended = true; + // $response->status($httpCode); + if ($httpCode == 200) { + $socket->send("HTTP/1.1 {$httpCode} OK\r\n"); + } else { + $socket->send("HTTP/1.1 {$httpCode} ERROR\r\n"); + } + + foreach (array_merge([ + 'Content-Type' => 'application/octet-stream', + 'X-Accel-Buffering' => 'no', + 'X-Server' => 'webserver/1.0' + ], $headers) as $k => $v) { + $socket->send("{$k}: {$v}\r\n"); + } + $socket->send("\r\n"); + } + return strlen($str); + }); + + curl_exec($curl); + curl_close($curl); + + return $socket->close(); + }); + + $server->set([ + 'http_compression' => false, + 'http_parse_post' => false, + 'http_parse_files' => false, + ]); + go(function () use ($server) { + $server->start(); + }); + go(function () use ($server) { + if (System::waitSignal(SIGTERM)) { + $server->shutdown(); + } + }); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/compression_types.phpt b/tests/swoole_http_server_coro/compression_types.phpt new file mode 100644 index 00000000000..cffeeea6c1e --- /dev/null +++ b/tests/swoole_http_server_coro/compression_types.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server_coro: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http_compression_types_test($pm); + echo "DONE\n"; + file_get_contents('http://127.0.0.1:' . $pm->getFreePort() . '/shutdown'); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Server("127.0.0.1", $pm->getFreePort(), false); + $server->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + ]); + $server->handle('/', function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/error_404.phpt b/tests/swoole_http_server_coro/error_404.phpt index 142b572d5b6..97ee4a6128f 100644 --- a/tests/swoole_http_server_coro/error_404.phpt +++ b/tests/swoole_http_server_coro/error_404.phpt @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/error_413.phpt b/tests/swoole_http_server_coro/error_413.phpt index 572339ee69d..c0ef8582085 100644 --- a/tests/swoole_http_server_coro/error_413.phpt +++ b/tests/swoole_http_server_coro/error_413.phpt @@ -33,6 +33,6 @@ go(function () { file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/form_data_1.phpt b/tests/swoole_http_server_coro/form_data_1.phpt new file mode 100644 index 00000000000..8202c0a2a6a --- /dev/null +++ b/tests/swoole_http_server_coro/form_data_1.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_http_server_coro: form data 1 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test($pm, [250]); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end(json_encode($request->post)); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/form_data_2.phpt b/tests/swoole_http_server_coro/form_data_2.phpt new file mode 100644 index 00000000000..2073d3ddd10 --- /dev/null +++ b/tests/swoole_http_server_coro/form_data_2.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_http_server_coro: form data 2 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test($pm, [225]); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end(json_encode($request->post)); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/graceful_shutdown.phpt b/tests/swoole_http_server_coro/graceful_shutdown.phpt new file mode 100644 index 00000000000..b1069a20cb6 --- /dev/null +++ b/tests/swoole_http_server_coro/graceful_shutdown.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http_server_coro: graceful shutdown +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/sleep") . PHP_EOL; + }); + + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; + try { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + } catch(Throwable $e) { + Assert::contains($e->getMessage(), 'Connection reset by peer'); + echo "done\n"; + } + }); + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/', function ($request, $response) { + $response->end("index"); + }); + $server->handle('/sleep', function ($request, $response) { + Co::sleep(0.2); + $response->end("sleep"); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +index +shutdown +sleep +done diff --git a/tests/swoole_http_server_coro/handle.phpt b/tests/swoole_http_server_coro/handle.phpt index b8fc6246858..6beadcc990c 100644 --- a/tests/swoole_http_server_coro/handle.phpt +++ b/tests/swoole_http_server_coro/handle.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/https.phpt b/tests/swoole_http_server_coro/https.phpt index f4ee166845b..c8a6c8c1180 100644 --- a/tests/swoole_http_server_coro/https.phpt +++ b/tests/swoole_http_server_coro/https.phpt @@ -28,7 +28,7 @@ go(function () use ($port) { echo httpGetBody("https://127.0.0.1:{$port}/") . PHP_EOL; echo httpGetBody("https://127.0.0.1:{$port}/stop?hello=1") . PHP_EOL; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/ipv6.phpt b/tests/swoole_http_server_coro/ipv6.phpt index 2a00c11e04d..3dd4288a0f9 100644 --- a/tests/swoole_http_server_coro/ipv6.phpt +++ b/tests/swoole_http_server_coro/ipv6.phpt @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/keepalive.phpt b/tests/swoole_http_server_coro/keepalive.phpt index 11203fcc217..1a7ec87be53 100644 --- a/tests/swoole_http_server_coro/keepalive.phpt +++ b/tests/swoole_http_server_coro/keepalive.phpt @@ -35,6 +35,6 @@ go(function () { file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/pipeline.phpt b/tests/swoole_http_server_coro/pipeline.phpt index 8045cb24a6e..337dcff8c44 100644 --- a/tests/swoole_http_server_coro/pipeline.phpt +++ b/tests/swoole_http_server_coro/pipeline.phpt @@ -19,6 +19,7 @@ Coroutine\run(function () { Coroutine::create(function () use ($server) { $server->handle('/', function (Request $request, Response $response) use ($server) { static $count = 0; + $response->end("OK\n"); if (++$count === MAX_CONCURRENCY * MAX_REQUESTS) { echo "DONE\n"; $server->shutdown(); @@ -36,6 +37,9 @@ Coroutine\run(function () { } else { throw new RuntimeException('Connect failed: ' . $socket->errMsg); } + while (!empty($socket->recv())) { + // pass + } }); } }); diff --git a/tests/swoole_http_server_coro/remote_addr.phpt b/tests/swoole_http_server_coro/remote_addr.phpt index 45c0ca03faa..96c0fc26675 100644 --- a/tests/swoole_http_server_coro/remote_addr.phpt +++ b/tests/swoole_http_server_coro/remote_addr.phpt @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/restart.phpt b/tests/swoole_http_server_coro/restart.phpt new file mode 100644 index 00000000000..845f920beb5 --- /dev/null +++ b/tests/swoole_http_server_coro/restart.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_http_server_coro: graceful shutdown +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $n = 2; + while ($n--) { + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; + usleep(150000); + } + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/', function ($request, $response) { + $response->end("index"); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + + $n = 2; + while ($n--) { + $server->start(); + usleep(100000); + } + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +[1]index +[1]shutdown +[0]index +[0]shutdown diff --git a/tests/swoole_http_server_coro/sendfile.phpt b/tests/swoole_http_server_coro/sendfile.phpt index 81315a01455..11528f05e13 100644 --- a/tests/swoole_http_server_coro/sendfile.phpt +++ b/tests/swoole_http_server_coro/sendfile.phpt @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/slow_client.phpt b/tests/swoole_http_server_coro/slow_client.phpt index bd607f9d014..9f15f8bb260 100644 --- a/tests/swoole_http_server_coro/slow_client.phpt +++ b/tests/swoole_http_server_coro/slow_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $html = base64_encode(random_bytes(rand(1024, 65536))); $len = strlen($html); $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"; - $chunks = str_split($data, 5); + $chunks = str_split($data, rand(5, 255)); foreach ($chunks as $out) { $client->send($out); usleep(100); diff --git a/tests/swoole_http_server_coro/slow_large_post.phpt b/tests/swoole_http_server_coro/slow_large_post.phpt new file mode 100644 index 00000000000..9ee1c7cddee --- /dev/null +++ b/tests/swoole_http_server_coro/slow_large_post.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_http_server_coro: slow large post +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.0.1", $pm->getFreePort()); + $post_data = KEY . '=' . urlencode(VALUE); + $len = strlen($post_data); + $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"; + + $client->send(substr($data, 0, N1)); + usleep(30000); + $client->send(substr($data, N1, N2)); + usleep(30000); + $client->send(substr($data, N1 + N2)); + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) +{ + run(function () use($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function (Request $request, Response $response) use ($pm) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + Assert::eq(VALUE, $request->post[KEY]); + $response->end("OK"); + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server_coro/ssl_bad_client.phpt b/tests/swoole_http_server_coro/ssl_bad_client.phpt index bb0a0a008e1..39e6a69b53e 100644 --- a/tests/swoole_http_server_coro/ssl_bad_client.phpt +++ b/tests/swoole_http_server_coro/ssl_bad_client.phpt @@ -12,8 +12,7 @@ $pm->parentFunc = function () use ($pm) { Co\run(function () use ($pm) { $port = $pm->getFreePort(); $client = new Co\Client(SWOOLE_SOCK_TCP); //同步阻塞 - if (!$client->connect('127.0.0.1', $port)) - { + if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); } $client->send('hello world'); diff --git a/tests/swoole_http_server_coro/tcp_nodelay.phpt b/tests/swoole_http_server_coro/tcp_nodelay.phpt index 55528c22dfa..ccd7e670abf 100644 --- a/tests/swoole_http_server_coro/tcp_nodelay.phpt +++ b/tests/swoole_http_server_coro/tcp_nodelay.phpt @@ -25,7 +25,7 @@ go(function () use ($port) { echo httpGetBody("http://127.0.0.1:{$port}/") . PHP_EOL; echo httpGetBody("http://127.0.0.1:{$port}/stop?hello=1") . PHP_EOL; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/upload.phpt b/tests/swoole_http_server_coro/upload.phpt index 3e94c2bd303..b815236cb60 100644 --- a/tests/swoole_http_server_coro/upload.phpt +++ b/tests/swoole_http_server_coro/upload.phpt @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); }); $pm->wakeup(); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_http_server_coro/websocket.phpt b/tests/swoole_http_server_coro/websocket.phpt index 46f3eca029e..4f50b2ed708 100644 --- a/tests/swoole_http_server_coro/websocket.phpt +++ b/tests/swoole_http_server_coro/websocket.phpt @@ -15,18 +15,20 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/websocket'); Assert::assert($ret); - $data = sha1(get_safe_random(mt_rand(0, 1024))); + $data = sha1(get_safe_random(mt_rand(1, 1024))); for ($n = MAX_REQUESTS; $n--;) { $cli->push($data); $ret = $cli->recv(); Assert::same($ret->data, "Hello {$data}!"); $ret = $cli->recv(); Assert::same($ret->data, "How are you, {$data}?"); + Assert::same($cli->cookies['test-file'], __FILE__); + Assert::same($cli->headers['x-swoole'], 'hello'); $count++; } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS)); $pm->kill(); }; @@ -34,6 +36,8 @@ $pm->childFunc = function () use ($pm) { go(function () use ($pm) { $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); $server->handle('/websocket', function ($request, $ws) { + $ws->header('x-swoole', 'hello'); + $ws->cookie('test-file', __FILE__); $ws->upgrade(); while (true) { $frame = $ws->recv(); @@ -57,7 +61,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_close.phpt b/tests/swoole_http_server_coro/websocket_close.phpt index 8d187e5104a..2e9cbbd80db 100644 --- a/tests/swoole_http_server_coro/websocket_close.phpt +++ b/tests/swoole_http_server_coro/websocket_close.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { Assert::lessThan(microtime(true) - $s, 0.002); Assert::same($ret, false); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_ping_pong.phpt b/tests/swoole_http_server_coro/websocket_ping_pong.phpt index 6a1703fe8dd..4c68cf64c92 100644 --- a/tests/swoole_http_server_coro/websocket_ping_pong.phpt +++ b/tests/swoole_http_server_coro/websocket_ping_pong.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $ret = $cli->recv(); Assert::same($ret->opcode, WEBSOCKET_OPCODE_PONG); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -62,7 +62,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); $pm->wakeup(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_iouring/mix.phpt b/tests/swoole_iouring/mix.phpt new file mode 100644 index 00000000000..497459d35ea --- /dev/null +++ b/tests/swoole_iouring/mix.phpt @@ -0,0 +1,194 @@ +--TEST-- +swoole_runtime/file_hook: support io_uring +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, +]; + +if (defined('SWOOLE_IOURING_SQPOLL')) { + $setting['iouring_flag'] = SWOOLE_IOURING_SQPOLL; +} + +swoole_async_set($setting); + +$results = []; +for ($i = 1; $i <= 10000; $i++) { + $results[$i] = random_bytes(rand(8192, 8192 * 3)); +} + +run(function() use ($results) { + $filesize = 1048576; + $content = random_bytes($filesize); + $fileName = '/tmp/test_file'; + Assert::eq(file_put_contents($fileName, $content), 1048576); + var_dump(stat($fileName)); + for ($i = 0; $i < 100; $i++) { + Assert::eq(filesize($fileName), 1048576); + Assert::eq(file_get_contents($fileName), $content); + } + unlink($fileName); + Assert::true(!file_exists($fileName)); + + $stream = fopen($fileName, 'w'); + fwrite($stream, $content); + if (PHP_VERSION_ID >= 80100) { + Assert::true(fdatasync($stream)); + Assert::true(fsync($stream)); + } + Assert::eq(file_get_contents($fileName), $content); + var_dump(fstat($stream)); + fclose($stream); + unlink($fileName); + + file_put_contents($fileName, $content); + rename($fileName, $fileName.'aaa'); + Assert::true(!file_exists($fileName)); + Assert::true(file_exists($fileName.'aaa')); + unlink($fileName.'aaa'); + + $directory = '/tmp/a/b/c/d/e/f'; + mkdir($directory, 0755, true); + Assert::true(is_dir($directory)); + rmdir($directory); + Assert::true(!is_dir($directory)); + + $waitGroup = new WaitGroup(); + for ($i = 1; $i <= 10000; $i++) { + go(function() use ($waitGroup, $i, $results){ + $waitGroup->add(); + file_put_contents('/tmp/file'.$i, $results[$i]); + Assert::true($results[$i] == file_get_contents('/tmp/file'.$i)); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + Assert::true(strlen($results[$i]) * 3 == strlen(file_get_contents('/tmp/file'.$i))); + $waitGroup->done(); + }); + } + $waitGroup->wait(); + echo 'SUCCESS'; +}); +?> +--EXPECTF-- +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +SUCCESS diff --git a/tests/swoole_iouring/setting.phpt b/tests/swoole_iouring/setting.phpt new file mode 100644 index 00000000000..ff3dcd290e8 --- /dev/null +++ b/tests/swoole_iouring/setting.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_http_server: iouring setting test +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + 'iouring_entries' => 4096, + 'iouring_workers' => 16 + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->status(200, "status"); + $response->end("Hello World"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_library/name_resolver/1.phpt b/tests/swoole_library/name_resolver/1.phpt new file mode 100644 index 00000000000..191b441960e --- /dev/null +++ b/tests/swoole_library/name_resolver/1.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_library/name_resolver: resolve +--SKIPIF-- + +--FILE-- + [$ns]]); + +$html = base64_encode(random_bytes(rand(2048, 65536 * 2))); + +$pm = new SwooleTest\ProcessManager; +$pm->initFreePorts(PORT_N); + +$pm->parentFunc = function ($pid) use ($pm, $ns, $html) { + Coroutine::set(['name_resolver' => [$ns]]); + run(function () use ($html) { + swoole_loop_n(REQ_N, function () use ($html) { + $client = new Client(SERVICE_NAME); + $client->set(['max_retries' => PORT_N]); + $r = $client->get('/'); + Assert::true($r); + Assert::eq($client->getBody(), $html); + }); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $ns, $html) { + $serv = new Server('127.0.0.1', $pm->getFreePort(0), SERVER_MODE_RANDOM); + $serv->addListener('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on("workerStart", function ($serv, $workerId) use ($pm, $ns) { + if ($workerId == 0) { + swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) { + $ns->join(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i)); + }); + $pm->wakeup(); + } + }); + $serv->on('request', function ($req, $resp) use ($pm, $html) { + if ($req->server['server_port'] == $pm->getFreePort(1)) { + $resp->status(503); + $resp->end(); + return; + } + $resp->end($html); + }); + $serv->on('beforeShutdown', function ($serv) use ($pm, $ns) { + swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) { + $ns->leave(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i)); + }); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_library/name_resolver/lookup.phpt b/tests/swoole_library/name_resolver/lookup.phpt new file mode 100644 index 00000000000..e45faa042e5 --- /dev/null +++ b/tests/swoole_library/name_resolver/lookup.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_library/name_resolver: lookup +--SKIPIF-- + +--FILE-- + [$ns]]); + +const N = 4; + +run(function () use ($ns) { + $test_name = 'test_resolver_1'; + $nodes = []; + swoole_loop_n(N, function () use (&$nodes, $test_name, $ns) { + $node = ['port' => rand(1, 9999), 'ip' => '192.168.1.' . rand(1, 255)]; + $nodes[] = $node; + $ns->join($test_name, $node['ip'], $node['port']); + }); + + $ctx = new Swoole\NameResolver\Context(AF_INET, true); + swoole_loop_n(N * 2, function ($i) use (&$nodes, $test_name, $ns, $ctx) { + $rs = swoole_name_resolver_lookup($test_name, $ctx); + Assert::notEmpty($rs); + [$ip, $port] = explode(':', $rs); + $node = ['ip' => $ip, 'port' => $port]; + Assert::true(in_array($node, $nodes)); + }); + + swoole_loop_n(N, function ($i) use (&$nodes, $test_name, $ns) { + $ns->leave($test_name, $nodes[$i]['ip'], $nodes[$i]['port']); + }); +}); +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_lock/mutex.phpt b/tests/swoole_lock/mutex.phpt index 7f9140c415d..29e1b832d1c 100644 --- a/tests/swoole_lock/mutex.phpt +++ b/tests/swoole_lock/mutex.phpt @@ -9,12 +9,13 @@ require __DIR__ . '/../include/skipif.inc'; require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; +use Swoole\Lock; $fp = STDOUT; -$lock = new Swoole\Lock(SWOOLE_MUTEX); +$lock = new Lock(SWOOLE_MUTEX); $pid = posix_getpid(); -fwrite($fp, "[Master]create lock\n"); +fwrite($fp, "[Master] Create Lock\n"); $lock->lock(); $process1 = new Process(function ($p) use ($lock, $fp) { @@ -40,7 +41,7 @@ Process::wait(); Process::wait(); ?> --EXPECTF-- -[Master]create lock +[Master] Create Lock [Child 1] Wait Lock [Child 2] Sleep [Child 2] Release Lock diff --git a/tests/swoole_lock/mutex_robust.phpt b/tests/swoole_lock/mutex_robust.phpt index 050f74d0852..3781e4f3e12 100644 --- a/tests/swoole_lock/mutex_robust.phpt +++ b/tests/swoole_lock/mutex_robust.phpt @@ -9,6 +9,9 @@ skip("no supports"); parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm, $fp) { - $lock = new Swoole\Lock(SWOOLE_MUTEX); + $lock = new Lock(SWOOLE_MUTEX); $pid = posix_getpid(); - fwrite($fp, "[Master {$pid}]create lock\n"); + fwrite($fp, "[Master {$pid}] Create Lock\n"); $lock->lock(); $n = 2; while ($n--) { - $process = new Swoole\Process(function ($p) use ($lock, $fp) { + $process = new Process(function ($p) use ($lock, $fp) { fwrite($fp, "[Child {$p->pid}] Wait Lock\n"); $lock->lock(); fwrite($fp, "[Child {$p->pid}] Get Lock\n"); @@ -44,7 +47,7 @@ echo file_get_contents($file); unlink($file); ?> --EXPECTF-- -[Master %d]create lock +[Master %d] Create Lock [Child %d] Wait Lock [Child %d] Wait Lock [Child %d] Get Lock diff --git a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt b/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt deleted file mode 100644 index 8fd35036910..00000000000 --- a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - for ($n = MAX_REQUESTS; $n--;) { - $statement = $mysql->prepare('SELECT ?'); - $statement = null; - Co::sleep(0.001); - $result = $mysql->query('show status like \'Prepared_stmt_count\''); - assert($result[0]['Value'] === '0'); - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/aborted_clients.phpt b/tests/swoole_mysql_coro/aborted_clients.phpt deleted file mode 100644 index 0b1b14f42f5..00000000000 --- a/tests/swoole_mysql_coro/aborted_clients.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql-close/reconnect/aborted-client-num ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::true($db->connect($server)); - $before_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::true($db->close()); - Assert::true($db->connect($server)); - $after_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::same($after_num - $before_num, 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt deleted file mode 100644 index e2c6632fbb0..00000000000 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal another coroutine ---SKIPIF-- - ---FILE-- -query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - } - - $cli = new Co\MySQL; - $connected = $cli->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($connected)) { - go(function () use ($cli) { - $cli->query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - }); - go(function () use ($cli) { - (function () use ($cli) { - (function () use ($cli) { - get($cli); - })(); - })(); - }); - } - }); -}, false, null, false); -$process->start(); -Swoole\Process::wait(); -?> ---EXPECTF-- -Fatal 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 -Stack trace: -#0 %s(%d): Swoole\Coroutine\MySQL->query('SELECT SLEEP(%d)') -#1 %s(%d): get(Object(Swoole\Coroutine\MySQL)) -#2 %s(%d): {closure}() -#3 %s(%d): {closure}() -#4 {main} - thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/bc_fetchAll.phpt b/tests/swoole_mysql_coro/bc_fetchAll.phpt deleted file mode 100644 index 02a3d15d1cc..00000000000 --- a/tests/swoole_mysql_coro/bc_fetchAll.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql fetchAll should return empty array (#2674) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - defer(function () use ($server, $client) { - $client->connect($server); - $client->query('DROP TABLE `empty`'); - }); - if (Assert::true($client->query("CREATE TABLE `empty` (`id` int(11))"))) { - // query - Assert::notEmpty($client->query('SELECT * FROM `ckl`')); - Assert::same($client->query('SELECT * FROM `empty`'), []); - Assert::same($client->query('SELECT * FROM `notexist`'), false); - // execute - Assert::notEmpty($client->prepare('SELECT * FROM `ckl`')->execute()); - Assert::same(($statement = $client->prepare('SELECT * FROM `empty`'))->execute(), []); - Assert::same($client->prepare('SELECT * FROM `notexist`'), false); - // closed - Assert::true($client->close()); - Assert::same($client->query('SELECT * FROM `empty`'), false); - Assert::same($client->prepare('SELECT * FROM `empty`'), false); - Assert::same($statement->execute(), false); - - if (Assert::true($client->connect($server + ['fetch_mode' => true]))) { - // query - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::notEmpty($client->fetch()); - Assert::null($client->fetch()); - Assert::null($client->fetch()); - Assert::same($client->fetchAll(), []); - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::count($client->fetchAll(), 1); - Assert::same($client->fetchAll(), []); - // execute - Assert::isInstanceOf( - $statement = $client->prepare('SELECT * FROM `ckl` LIMIT 1'), - Swoole\Coroutine\MySQL\Statement::class - ); - Assert::same($statement->fetchAll(), []); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetch()); - Assert::null($statement->fetch()); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetchAll()); - Assert::same($statement->fetchAll(), []); - // closed - Assert::true($client->close()); - Assert::false($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::false($client->fetch()); - Assert::false($client->fetchAll()); - Assert::false($statement->execute()); - Assert::false($statement->fetch()); - Assert::false($statement->fetchAll()); - echo "DONE\n"; - } - } - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/bc_sync_properties.phpt b/tests/swoole_mysql_coro/bc_sync_properties.phpt deleted file mode 100644 index a23866ff9da..00000000000 --- a/tests/swoole_mysql_coro/bc_sync_properties.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - /* @var $statement Swoole\Coroutine\MySQL\Statement */ - $statement = $client->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class)) { - if (Assert::true($statement->execute(['www.baidu.com', '/search', 'baidu']))) { - Assert::same($statement->affected_rows, 1); - Assert::greaterThan($statement->insert_id, 0); - Assert::same($client->affected_rows, $statement->affected_rows); - Assert::same($client->insert_id, $statement->insert_id); - if (Assert::false($statement->execute())) { - Assert::same($statement->errno, SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO); - Assert::same($client->error, $statement->error); - Assert::same($client->errno, $statement->errno); - } - echo "SUCCESS\n"; - } - } - } -}); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_mysql_coro/big_data.phpt b/tests/swoole_mysql_coro/big_data.phpt deleted file mode 100644 index 02f6b3f6f87..00000000000 --- a/tests/swoole_mysql_coro/big_data.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: select big data from db ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - if (Assert::assert($db->query($createTable))) { - $statement = $db->prepare("INSERT INTO {$table_name} VALUES (?, ?)"); - $random = []; - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $random[$n] = str_repeat(get_safe_random(256), 128); // 32K - $ret = $statement->execute([$n + 1, $random[$n]]); - Assert::assert($ret); - } - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $ret = $statement->execute(); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - Assert::same($ret[$n]['content'], $random[$n]); - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/bug_0814.phpt b/tests/swoole_mysql_coro/bug_0814.phpt deleted file mode 100644 index a3626e69814..00000000000 --- a/tests/swoole_mysql_coro/bug_0814.phpt +++ /dev/null @@ -1,72 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[1] ERROR\n"; - return; - } - - /** - * 第一次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); - - $s = microtime(true); - $ret = $db->query("select sleep(20)", 0.1); - time_approximate(0.1, microtime(true) - $s); - Assert::false($ret); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[2] ERROR\n"; - return; - } - - /** - * 第二次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/connect_timeout.phpt b/tests/swoole_mysql_coro/connect_timeout.phpt deleted file mode 100644 index e9f4954dde0..00000000000 --- a/tests/swoole_mysql_coro/connect_timeout.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: connect timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => '192.0.0.1', - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$connected); - Assert::assert($mysql->connected === false); - Assert::assert($mysql->connect_errno === SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - // handshake timeout - $s = microtime(true); - $connected = $mysql->connect([ - 'host' => REDIS_SERVER_HOST, - 'port' => REDIS_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::false($connected); - Assert::same($mysql->connected, false); - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/db_destruct.phpt b/tests/swoole_mysql_coro/db_destruct.phpt deleted file mode 100644 index 53d102077f8..00000000000 --- a/tests/swoole_mysql_coro/db_destruct.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql db destruct ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $statement = $db->prepare('SELECT 1'); - Assert::isInstanceOf($statement, Co\Mysql\Statement::class); - $ret = $statement->execute(); - Assert::same($ret[0][1], 1); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/defer_and_fetch.phpt b/tests/swoole_mysql_coro/defer_and_fetch.phpt deleted file mode 100644 index 758f3459825..00000000000 --- a/tests/swoole_mysql_coro/defer_and_fetch.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql defer and fetch ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]); - $mysql->setDefer(true); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - if ($n === 0 || mt_rand(0, 1)) { - $ret = $mysql->prepare('SELECT ?+?'); - Assert::true($ret); - $statement = $mysql->recv(); - Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class); - } - $a = mt_rand(0, 65535); - $b = mt_rand(0, 65535); - /** @var $statement Swoole\Coroutine\MySQL\Statement */ - Assert::true($statement->execute([$a, $b])); - Assert::true($statement->recv()); - $result = $statement->fetchAll(); - if (Assert::isArray($result)) { - Assert::same(reset($result[0]), (float)($a + $b)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/err_instead_of_eof.phpt b/tests/swoole_mysql_coro/err_instead_of_eof.phpt deleted file mode 100644 index d453be39b51..00000000000 --- a/tests/swoole_mysql_coro/err_instead_of_eof.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: ERR Instead of EOF ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - if (!$db->query("EXPLAIN SELECT * FROM dual;")) { - echo $db->errno . PHP_EOL; - echo $db->error . PHP_EOL; - } -}); -?> ---EXPECT-- -1096 -SQLSTATE[HY000] [1096] No tables used diff --git a/tests/swoole_mysql_coro/escape.phpt b/tests/swoole_mysql_coro/escape.phpt deleted file mode 100644 index a58987ae935..00000000000 --- a/tests/swoole_mysql_coro/escape.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql escape ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::same($mysql->escape(""), ""); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch.phpt b/tests/swoole_mysql_coro/fetch.phpt deleted file mode 100644 index 3c794d133e3..00000000000 --- a/tests/swoole_mysql_coro/fetch.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt = $db->prepare('SELECT `id` FROM `userinfo` LIMIT 2'); - Assert::true($stmt->execute()); - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH1 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH2 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - Assert::same($stmt->fetch(), null); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode.phpt b/tests/swoole_mysql_coro/fetch_mode.phpt deleted file mode 100644 index 5000a2a8cb8..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt1 = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt1->execute()); - $stmt2 = $db->prepare('SELECT * FROM ckl LIMIT 2'); - Assert::true($stmt2->execute()); - Assert::same(count($stmt1->fetchAll()), 1); - Assert::same(count($stmt2->fetchAll()), 2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode_twice.phpt b/tests/swoole_mysql_coro/fetch_mode_twice.phpt deleted file mode 100644 index 1a312c4b9ee..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode_twice.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: call fetch twice ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - Assert::true($db->query("INSERT INTO ckl (`domain`,`path`,`name`) VALUES ('www.baidu.com', '/search', 'baidu')")); - // now we can make the responses independent - $stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt->execute()); - Assert::assert(($ret = $stmt->fetchAll()) && is_array($ret) && count($ret) === 1); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/illegal_extends.phpt b/tests/swoole_mysql_coro/illegal_extends.phpt deleted file mode 100644 index fa897fc0d8c..00000000000 --- a/tests/swoole_mysql_coro/illegal_extends.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal child class ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - // invalid connect - Assert::true($db->connect($server)); - Assert::false($db->connected); - Assert::false($db->query('select 1')); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - - // right implementation - Assert::true($db->connectRaw($server)); - Assert::same($db->query('select 1')[0][1], 1); -}); - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/invalid_host.phpt b/tests/swoole_mysql_coro/invalid_host.phpt deleted file mode 100644 index 2c5a945fb4f..00000000000 --- a/tests/swoole_mysql_coro/invalid_host.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: invalid host ---SKIPIF-- - ---FILE-- -connect([ - 'host' => get_safe_random(), - 'port' => MYSQL_SERVER_PORT, - 'database' => MYSQL_SERVER_DB, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'timeout' => 0.5 - ]); - echo 'Connection: ' . ($connected ? 'Connected' : 'Not connected') . PHP_EOL; - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - echo $mysql->connect_error . PHP_EOL; -}); -?> ---EXPECTF-- -Connection: Not connected -SQLSTATE[HY000] [2002] %s diff --git a/tests/swoole_mysql_coro/kill_process.phpt b/tests/swoole_mysql_coro/kill_process.phpt deleted file mode 100644 index 025cad9bc93..00000000000 --- a/tests/swoole_mysql_coro/kill_process.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_mysql_coro: kill process and check liveness ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - $mysql = new Swoole\Coroutine\MySQL; - Assert::true($mysql->connect($config)); - Assert::same($mysql->query('SELECT 1')[0][1], 1); - - $killer = new Swoole\Coroutine\MySQL; - Assert::true($killer->connect($config)); - - foreach ( - [ - function () use ($mysql) { - return $mysql->query('SELECT 1'); - }, - function () use ($mysql) { - return $mysql->begin(); - }, - function () use ($mysql) { - return $mysql->prepare('SELECT 1'); - }, - ] as $command - ) { - $processList = $killer->query('show processlist'); - $processList = array_filter($processList, function (array $value) { - return $value['db'] == MYSQL_SERVER_DB && $value['Info'] != 'show processlist'; - }); - foreach ($processList as $process) { - $killer->query("KILL {$process['Id']}"); - } - switch_process(); - Assert::false($command()); - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - Assert::true($mysql->connect($config)); - } - - echo $mysql->error . PHP_EOL; -}); -echo "DONE\n"; -?> ---EXPECT-- -SQLSTATE[HY000] [2006] MySQL server has gone away -DONE diff --git a/tests/swoole_mysql_coro/many_rows.phpt b/tests/swoole_mysql_coro/many_rows.phpt deleted file mode 100644 index 1742140cd0d..00000000000 --- a/tests/swoole_mysql_coro/many_rows.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: insert and select many rows ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - $row_num = [100, 200, 1000, 3000][PRESSURE_LEVEL]; - if (Assert::assert($db->query($createTable))) { - $sql = "INSERT INTO {$table_name} (`content`) VALUES " . rtrim(str_repeat('(?), ', $row_num), ', '); - $statement = $db->prepare($sql); - $random = []; - for ($n = 0; $n < $row_num; $n++) { - $random[$n] = get_safe_random(64); - } - $statement->execute($random); - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $result = $statement->execute(); - if (Assert::assert(count($result) === $row_num)) { - for ($n = 0; $n < $row_num; $n++) { - Assert::same($result[$n]['content'], $random[$n]); - } - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/multi_packets.phpt b/tests/swoole_mysql_coro/multi_packets.phpt deleted file mode 100644 index c6245ee2986..00000000000 --- a/tests/swoole_mysql_coro/multi_packets.phpt +++ /dev/null @@ -1,125 +0,0 @@ ---TEST-- -swoole_mysql_coro: select and insert huge data from db (10M~64M) ---SKIPIF-- - ---FILE-- - -1 -]); -go(function () { - $mysql = new Swoole\Coroutine\Mysql; - $mysql_server = [ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - // set max_allowed_packet - $mysql->connect($mysql_server); - if (!$mysql->query('set global max_allowed_packet = 100 * 1024 * 1024')) { - exit('unable to set max_allowed_packet to 100M.'); - } - // reconnect and we can see changes - $mysql->close(); - $mysql->connect($mysql_server); - @$mysql->query('DROP TABLE `firmware`'); - $ret = $mysql->query(<<error); - } - $max_allowed_packet = $mysql->query('show VARIABLES like \'max_allowed_packet\''); - $max_allowed_packet = $max_allowed_packet[0]['Value'] / 1024 / 1024; - phpt_var_dump("max_allowed_packet: {$max_allowed_packet}M"); - if (IS_IN_TRAVIS) { - $max_allowed_packet = 36; - } else { - $max_allowed_packet = 64; - } - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $mysql_query = new Swoole\Coroutine\Mysql; - $mysql_query->connect($mysql_server); - $mysql_prepare = new Swoole\Coroutine\Mysql; - $mysql_prepare->connect($mysql_server); - for ($fid = 1; $fid <= $max_allowed_packet / 10; $fid++) { - $random_size = 2 << mt_rand(2, 9); - $text_size = min($fid * 10 + mt_rand(1, 9), $max_allowed_packet) * 1024 * 1024; // 1xM ~ 5xM - $firmware = str_repeat(get_safe_random($random_size), $text_size / $random_size); - $f_md5 = md5($firmware); - $f_remark = get_safe_random(); - if (mt_rand(0, 1)) { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) " . - "VALUES ({$fid}, '{$firmware}', '{$f_md5}', '{$f_remark}')"; - $ret = $mysql_query->query($sql); - } else { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) VALUES (?, ?, ?, ?)"; - $pdo_stmt = $mysql_prepare->prepare($sql); - if ($pdo_stmt) { - $ret = $pdo_stmt->execute([$fid, $firmware, $f_md5, $f_remark]); - if (!$ret) { - var_dump($pdo_stmt); - exit; - } - } else { - $ret = false; - } - } - if (Assert::assert($ret)) { - $sql = 'SELECT * FROM `test`.`firmware` WHERE fid='; - $pdo_stmt = $pdo->prepare("{$sql}?"); - $mysql_stmt = $mysql_prepare->prepare("{$sql}?"); - $chan = new Chan(); - go(function () use ($chan, $pdo_stmt, $fid) { - $pdo_stmt->execute([$fid]); - $result = $pdo_stmt->fetch(PDO::FETCH_ASSOC); - $chan->push(['pdo', $result]); - }); - go(function () use ($chan, $mysql_stmt, $fid) { - $result = $mysql_stmt->execute([$fid])[0]; - $chan->push(['mysql_prepare', $result]); - }); - go(function () use ($chan, $mysql_query, $sql, $fid) { - $chan->push(['mysql_query', $mysql_query->query("{$sql}{$fid}")[0]]); - }); - for ($i = 3; $i--;) { - list($from, $result) = $chan->pop(); - if ($result['fid'] === $fid) { - Assert::same($result['firmware'], $firmware); - Assert::same($result['f_md5'], $f_md5); - Assert::same($result['f_remark'], $f_remark); - } else { - Assert::assert(0, 'wrong result from ' . $from); - unset($result['firmware']); // too long to show - phpt_var_dump($result); - } - phpt_var_dump(sprintf('%-16s: %s', $from, (strlen($firmware) / 1024 / 1024) . 'M')); - } - } - } - echo "DONE\n"; -}); -swoole_event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/not_exist.phpt b/tests/swoole_mysql_coro/not_exist.phpt deleted file mode 100644 index 280b1fad61c..00000000000 --- a/tests/swoole_mysql_coro/not_exist.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect to wrong database ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => 'not_exist' - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - Assert::same($db->connect_errno, 1049); // unknown database - Assert::assert(strpos($db->connect_error, 'not_exist')); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/null.phpt b/tests/swoole_mysql_coro/null.phpt deleted file mode 100644 index 9f29d660ca9..00000000000 --- a/tests/swoole_mysql_coro/null.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]); - Assert::assert($connected); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES (NULL)')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("")')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("NULL")')); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); - - $stmt = $mysql->prepare('INSERT INTO `custom` (`content`) VALUES (?)'); - Assert::assert($stmt->execute([NULL])); - Assert::assert($stmt->execute([''])); - Assert::assert($stmt->execute(['NULL'])); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); -}); -?> ---EXPECT-- -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} diff --git a/tests/swoole_mysql_coro/null_bit_map.phpt b/tests/swoole_mysql_coro/null_bit_map.phpt deleted file mode 100644 index f8cdec902e0..00000000000 --- a/tests/swoole_mysql_coro/null_bit_map.phpt +++ /dev/null @@ -1,103 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null bit map rand test ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - return $mysql; -} - -for ($c = MAX_CONCURRENCY_LOW; $c--;) { - go(function () use ($c) { - // gen table structure - $table_name = 't' . substr(md5(mt_rand()), 0, 15); - $field_size = mt_rand(1, 100); - list($fields, $fields_info) = (function () use ($field_size) { - $fields_info = []; - $fields = ''; - for ($i = $field_size; $i--;) { - $info = $fields_info[] = [ - 'name' => 'f' . substr(md5(mt_rand()), 0, 7), - 'type' => gen_type() - ]; - $fields .= "{$info['name']} {$info['type']} NULL,\n"; - } - return [rtrim($fields, " \n,"), $fields_info]; - })(); - $mysql = mysql(); - // create table - $createTable = <<query($createTable)) { - trigger_error("create table error by query statement [{$createTable}]", E_USER_WARNING); - return; - } - $_insert = "INSERT INTO {$table_name} VALUES (" . rtrim(str_repeat('?, ', $field_size + 1), ', ') . ")"; - $data_list = []; - try { - for ($n = MAX_REQUESTS; $n--;) { - $insert = $mysql->prepare($_insert); - Assert::assert($insert instanceof Co\Mysql\Statement); - $data_list[] = $gen = (function ($id, $fields_info) { - $r = ['id' => $id]; - foreach ($fields_info as $info) { - if (mt_rand(0, 1)) { - $r[$info['name']] = null; - } else { - $r[$info['name']] = gen_data_from_type($info['type']); - } - } - return $r; - })($n + 1, $fields_info); - Assert::assert($insert->execute(array_values($gen))); - } - $result = $mysql->prepare("SELECT * FROM {$table_name}")->execute(); - Assert::same(array_reverse($data_list), $result); - } catch (Throwable $e) { - Assert::assert(0); - } finally { - Assert::assert($mysql->query("DROP TABLE {$table_name}")); - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/numbers.phpt b/tests/swoole_mysql_coro/numbers.phpt deleted file mode 100644 index 4e0cb4b3190..00000000000 --- a/tests/swoole_mysql_coro/numbers.phpt +++ /dev/null @@ -1,138 +0,0 @@ ---TEST-- -swoole_mysql_coro: floating point value precision and unsigned big int overflow ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => false - ]; - $db->connect($server); - $r_string1 = $db->query('SELECT * FROM numbers'); - $db->close(); - $server['strict_type'] = true; - $db->connect($server); - $r_strong1 = $db->query('SELECT * FROM numbers'); - $stmt = $db->prepare('SELECT * FROM numbers'); - $r_strong2 = $stmt->execute(); - - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $r_string2 = $pdo->query('SELECT * FROM numbers')->fetchAll(PDO::FETCH_ASSOC); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare('SELECT * FROM numbers'); - $stmt->execute(); - $r_strong3 = $stmt->fetchAll(PDO::FETCH_ASSOC); - Assert::same($r_string1, $r_string2); - Assert::same($r_strong2, $r_strong3); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - - if (!is_musl_libc()) { - Assert::same($r_strong1, $r_strong2); - } - var_dump($r_strong2); -}); -?> ---EXPECT-- -array(3) { - [0]=> - array(13) { - ["id"]=> - int(1) - ["tinyint"]=> - int(127) - ["utinyint"]=> - int(255) - ["smallint"]=> - int(32767) - ["usmallint"]=> - int(65535) - ["mediumint"]=> - int(8388607) - ["umediumint"]=> - int(16777215) - ["int"]=> - int(2147483647) - ["uint"]=> - int(4294967294) - ["bigint"]=> - int(9223372036854775807) - ["ubigint"]=> - string(20) "18446744073709551615" - ["float"]=> - float(1.23457) - ["double"]=> - float(1.2345678901235) - } - [1]=> - array(13) { - ["id"]=> - int(2) - ["tinyint"]=> - int(-128) - ["utinyint"]=> - int(123) - ["smallint"]=> - int(-32768) - ["usmallint"]=> - int(12345) - ["mediumint"]=> - int(-8388608) - ["umediumint"]=> - int(123456) - ["int"]=> - int(-2147483648) - ["uint"]=> - int(123456) - ["bigint"]=> - int(-9223372036854775808) - ["ubigint"]=> - int(123456) - ["float"]=> - float(-1.23457) - ["double"]=> - float(-1.2345678901235) - } - [2]=> - array(13) { - ["id"]=> - int(3) - ["tinyint"]=> - int(0) - ["utinyint"]=> - int(0) - ["smallint"]=> - int(0) - ["usmallint"]=> - int(0) - ["mediumint"]=> - int(0) - ["umediumint"]=> - int(0) - ["int"]=> - int(0) - ["uint"]=> - int(0) - ["bigint"]=> - int(0) - ["ubigint"]=> - int(0) - ["float"]=> - float(1.23) - ["double"]=> - float(1.23) - } -} diff --git a/tests/swoole_mysql_coro/prepare_field_type.phpt b/tests/swoole_mysql_coro/prepare_field_type.phpt deleted file mode 100644 index 339ef2c6e11..00000000000 --- a/tests/swoole_mysql_coro/prepare_field_type.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare field type ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true, - ]; - - $ret1 = $db->connect($server); - if (! $ret1) { - echo "CONNECT ERROR\n"; - - return; - } - - $stmt = $db->prepare('SELECT ? as a, ? as b, ? as c, ? as d, ? + ? as e'); - if (! $stmt) { - echo "PREPARE ERROR\n"; - - return; - } - - $ret3 = $stmt->execute([123, 3.14, true, false, 11, 22]); - if (! $ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - - return; - } - if (Assert::isArray($ret3)) { - Assert::same(reset($ret3), ['a' => 123, 'b' => 3.14, 'c' => 1, 'd' => 0, 'e' => 33]); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_insert.phpt b/tests/swoole_mysql_coro/prepare_insert.phpt deleted file mode 100644 index 152a5e2dd83..00000000000 --- a/tests/swoole_mysql_coro/prepare_insert.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute(array('www.baidu.com', '/search', 'baidu')); - if (!$ret3) { - echo "EXECUTE ERROR\n"; - return; - } - Assert::assert($stmt->insert_id > 0); - Assert::assert($db->insert_id == $stmt->insert_id); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_multi.phpt b/tests/swoole_mysql_coro/prepare_multi.phpt deleted file mode 100644 index ca73c5eab19..00000000000 --- a/tests/swoole_mysql_coro/prepare_multi.phpt +++ /dev/null @@ -1,49 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare multi (insert and delete) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - $connected = $db->connect($server); - if (!$connected) { - echo "CONNECT ERROR\n"; - return; - } - for ($n = MAX_REQUESTS_MID; $n--;) { - $statement = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?, ?, ?)'); - if (!$statement) { - echo "PREPARE ERROR\n"; - return; - } - $executed = $statement->execute(['www.baidu.com', '/search', 'baidu']); - if (!$executed) { - echo "EXECUTE ERROR\n"; - return; - } - if ($statement->insert_id > 0) { - $deleted = $db->query("DELETE FROM ckl WHERE id={$statement->insert_id}"); - if (!$deleted) { - echo "DELETE ERROR\n"; - } - } else { - echo "INSERT ERROR\n"; - } - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/prepare_select.phpt b/tests/swoole_mysql_coro/prepare_select.phpt deleted file mode 100644 index 46ebc89112a..00000000000 --- a/tests/swoole_mysql_coro/prepare_select.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/procedure.phpt b/tests/swoole_mysql_coro/procedure.phpt deleted file mode 100644 index b0fe8966c93..00000000000 --- a/tests/swoole_mysql_coro/procedure.phpt +++ /dev/null @@ -1,60 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL reply(?)'); - for ($n = MAX_REQUESTS; $n--;) { - //SWOOLE - $_map = $map; - $res = $stmt->execute(['hello mysql!']); - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_by_query.phpt b/tests/swoole_mysql_coro/procedure_by_query.phpt deleted file mode 100644 index 4d1a7470dbd..00000000000 --- a/tests/swoole_mysql_coro/procedure_by_query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode by query ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS; $n--;) { - $_map = $map; - $res = $db->query('CALL reply("hello mysql!")'); - do { - if (is_array($res)) { - Assert::same(current($res[0]), array_shift($_map)); - } else { - Assert::true($res); - } - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_in_fetch.phpt b/tests/swoole_mysql_coro/procedure_in_fetch.phpt deleted file mode 100644 index fa1186fb6a9..00000000000 --- a/tests/swoole_mysql_coro/procedure_in_fetch.phpt +++ /dev/null @@ -1,82 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure in fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - - //SWOOLE - $_map = $map; - $stmt = $db->prepare('CALL reply(?)'); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - - //PDO - if (extension_loaded('PDO')) { - $_map = $map; - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare("CALL reply(?)"); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextRowset()); - Assert::same($stmt->rowCount(), 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_single.phpt b/tests/swoole_mysql_coro/procedure_single.phpt deleted file mode 100644 index cc876712e77..00000000000 --- a/tests/swoole_mysql_coro/procedure_single.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql procedure single ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL say(?)'); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $stmt->execute(['hello mysql!']); - Assert::same(current($ret[0]), 'You said: "hello mysql!"'); - Assert::null($stmt->nextResult()); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query.phpt b/tests/swoole_mysql_coro/procedure_with_query.phpt deleted file mode 100644 index 1fec3a58a6e..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without query (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - } - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt b/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt deleted file mode 100644 index 22a92273e5d..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: query 'CALL' statement & prepare (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $db->query('CALL sp_whoami()'); - Assert::null($db->nextResult()); - $stmt = $db->prepare('CALL sp_whoami()'); - $ret = $stmt->execute(); - Assert::assert(strpos(current($ret[0]), MYSQL_SERVER_USER) !== false); - Assert::null($stmt->nextResult()); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/query.phpt b/tests/swoole_mysql_coro/query.phpt deleted file mode 100644 index 45f3e6c4af3..00000000000 --- a/tests/swoole_mysql_coro/query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (\swoole_server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) - { - $mysql = new Swoole\Coroutine\MySQL(); - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $mysql->query('show tables', 2); - if (!$ret) { - goto fail; - } - if (count($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_mysql_coro/query_multifield.phpt b/tests/swoole_mysql_coro/query_multifield.phpt deleted file mode 100644 index b39c02da277..00000000000 --- a/tests/swoole_mysql_coro/query_multifield.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -swoole_mysql_coro: multi field ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $n = range(0, FIELD_NUM - 1); - $fields = implode(", ", $n); - $result = $db->query("select $fields"); - Assert::assert(count($result[0]) == FIELD_NUM); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/query_timeout.phpt b/tests/swoole_mysql_coro/query_timeout.phpt deleted file mode 100644 index 5105153f733..00000000000 --- a/tests/swoole_mysql_coro/query_timeout.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql query timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($ret)) { - $s = microtime(true); - $timeout = mt_rand(100, 500) / 1000; - $ret = $mysql->query('select sleep(1)', $timeout); - time_approximate($timeout, microtime(true) - $s); - if (Assert::false($ret)) { - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/readonly.phpt b/tests/swoole_mysql_coro/readonly.phpt deleted file mode 100644 index ca39b9b6d91..00000000000 --- a/tests/swoole_mysql_coro/readonly.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql use readonly user ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $connected = $root->connect($server); - Assert::assert($connected); - - // create read only user - $create = $root->query('CREATE USER `readonly`@`%` IDENTIFIED BY \'123456\';'); - Assert::assert($create); - $grant = $root->query('GRANT SELECT ON *.* TO `readonly`@`%` WITH GRANT OPTION;'); - Assert::assert($grant); - - // use readonly - $server['user'] = 'readonly'; - $server['password'] = '123456'; - $readonly = new Swoole\Coroutine\MySQL; - $connected = $readonly->connect($server); - Assert::assert($connected); - - // read - $result = $readonly->query('SELECT * FROM userinfo'); - Assert::assert(is_array($result) && count($result) > 5); - $id = $result[0]['id']; - // write - $delete = $readonly->query('DELETE FROM userinfo WHERE id=' . $id); - Assert::assert(!$delete); - echo $readonly->errno . "\n"; - echo $readonly->error . "\n"; - - // drop - Assert::assert($root->query('DROP ROLE readonly')); -}); -swoole_event::wait(); -?> ---EXPECTF-- -1142 -SQLSTATE[42000] [1142] DELETE command denied to user 'readonly'@'%s' for table 'userinfo' diff --git a/tests/swoole_mysql_coro/simple_query.phpt b/tests/swoole_mysql_coro/simple_query.phpt deleted file mode 100644 index 484696df28b..00000000000 --- a/tests/swoole_mysql_coro/simple_query.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql simple query ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($res); - $ret = $mysql->query('show tables', 2); - Assert::assert(is_array($ret)); - Assert::assert(count($ret) > 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_closed.phpt b/tests/swoole_mysql_coro/statement_closed.phpt deleted file mode 100644 index 2d908376287..00000000000 --- a/tests/swoole_mysql_coro/statement_closed.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - assert($ret); - $statement = $db->prepare('SELECT 1'); - assert($statement instanceof Co\Mysql\Statement); - $ret = $statement->execute(); - assert($ret[0][1] === 1); - $db->close(); - $ret = $db->connect($server); - assert($ret); - $ret = $statement->execute(); - assert(!$ret); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_destruct.phpt b/tests/swoole_mysql_coro/statement_destruct.phpt deleted file mode 100644 index a226edb2bab..00000000000 --- a/tests/swoole_mysql_coro/statement_destruct.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - - $start_prepared_num = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - $stmt1 = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt1) { - echo "PREPARE1 ERROR\n"; - return; - } - $stmt2 = $db->prepare('SELECT * FROM `userinfo`'); - if (!$stmt2) { - echo "PREPARE2 ERROR\n"; - return; - } - $stmt3 = $db->prepare('SELECT `id` FROM `userinfo`'); - if (!$stmt3) { - echo "PREPARE3 ERROR\n"; - return; - } - - $prepared_num1 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $start_prepared_num, 3); - $stmt1 = null; //destruct - unset($stmt2); //destruct - $prepared_num2 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $prepared_num2, 2); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/timeout.phpt b/tests/swoole_mysql_coro/timeout.phpt deleted file mode 100644 index 9c0e78abb92..00000000000 --- a/tests/swoole_mysql_coro/timeout.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - $statement = $mysql->prepare('SELECT SLEEP(1)'); - Assert::assert($statement instanceof Co\Mysql\Statement); - $timeout = ms_random(0.1, 0.5); - $s = microtime(true); - $use_query = !!mt_rand(0, 1); - if ($use_query) { - $ret = $mysql->query('SELECT SLEEP(1)', $timeout); - } else { - $ret = $statement->execute(null, $timeout); - } - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$ret); - Assert::same($use_query ? $mysql->errno : $statement->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/transaction.phpt b/tests/swoole_mysql_coro/transaction.phpt deleted file mode 100644 index 34b9ff47f09..00000000000 --- a/tests/swoole_mysql_coro/transaction.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_mysql_coro: transaction ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->rollback()); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert($db->commit()); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->query('DELETE FROM `ckl` WHERE `name`="' . $random . '"')); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - - $db->setDefer(); - Assert::throws(function () use ($db) { $db->begin(); }, Exception::class); - Assert::throws(function () use ($db) { $db->commit(); }, Exception::class); - Assert::throws(function () use ($db) { $db->rollback(); }, Exception::class); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/unixsocket.phpt b/tests/swoole_mysql_coro/unixsocket.phpt deleted file mode 100644 index 377ee006d04..00000000000 --- a/tests/swoole_mysql_coro/unixsocket.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connection on unix socket ---SKIPIF-- - ---FILE-- - 'unix:/' . MYSQL_SERVER_PATH, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - Assert::same($db->query('SELECT 1'), [['1' => '1']]); - echo "DONE\n"; -}); -?> ---EXPECTF-- -DONE diff --git a/tests/swoole_mysql_coro/userinfo.phpt b/tests/swoole_mysql_coro/userinfo.phpt deleted file mode 100644 index 3c5232dfe1d..00000000000 --- a/tests/swoole_mysql_coro/userinfo.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - $result = $mysql->query('SELECT * FROM `userinfo`'); - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo_result = $pdo->query('SELECT * FROM `userinfo`')->fetchAll(PDO::FETCH_ASSOC); - Assert::same($result, $pdo_result); - - $result = $mysql->prepare('SELECT * FROM `userinfo`')->execute(); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $pdo_stmt = $pdo->prepare('SELECT * FROM `userinfo`'); - $pdo_stmt->execute(); - $pdo_result =$pdo_stmt->fetchAll(PDO::FETCH_ASSOC); - - Assert::same($result, $pdo_result); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/without_fetch.phpt b/tests/swoole_mysql_coro/without_fetch.phpt deleted file mode 100644 index 253444f1a36..00000000000 --- a/tests/swoole_mysql_coro/without_fetch.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: just execute (test memory leak) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - $stmt = $db->prepare('SELECT * FROM `userinfo` LIMIT 1'); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::assert(is_array($stmt->fetchAll())); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/wrong_password.phpt b/tests/swoole_mysql_coro/wrong_password.phpt deleted file mode 100644 index 8b7e619254d..00000000000 --- a/tests/swoole_mysql_coro/wrong_password.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect with wrong password ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => 'i am hack', - 'database' => MYSQL_SERVER_DB - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - echo $db->connect_errno . "\n"; - echo $db->connect_error, "\n"; -}); -?> ---EXPECTF-- -1045 -SQLSTATE[28000] [1045] Access denied for user 'root'@'%s' (using password: YES) diff --git a/tests/swoole_mysql_coro/z_reset.phpt b/tests/swoole_mysql_coro/z_reset.phpt deleted file mode 100644 index fd307469210..00000000000 --- a/tests/swoole_mysql_coro/z_reset.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -swoole_mysql_coro: reset test mysql database ---SKIPIF-- - ---FILE-- - /dev/null`; -?> ---EXPECT-- diff --git a/tests/swoole_pdo_odbc/base.phpt b/tests/swoole_pdo_odbc/base.phpt new file mode 100644 index 00000000000..67875c9b165 --- /dev/null +++ b/tests/swoole_pdo_odbc/base.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_pdo_odbc: test hook pdo_odbc +--SKIPIF-- + +--FILE-- +prepare('show tables'); + $statement->execute(); + Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1); + }); + + Co\go(function () { + $pdo = new PDO(ODBC_DSN); + $statement = $pdo->prepare('show tables'); + $statement->execute(); + Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1); + }); +}); + +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_pdo_odbc/blocking.phpt b/tests/swoole_pdo_odbc/blocking.phpt new file mode 100644 index 00000000000..8d1e2acc162 --- /dev/null +++ b/tests/swoole_pdo_odbc/blocking.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_odbc: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + $statement->execute(); + Assert::eq($sleep_count, 0); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/query.phpt b/tests/swoole_pdo_odbc/query.phpt new file mode 100644 index 00000000000..c837ae377b9 --- /dev/null +++ b/tests/swoole_pdo_odbc/query.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_pdo_odbc: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)'); + + $list = []; + for ($i = 0; $i < N; $i++) { + $row = [ + 'name' => base64_encode(random_bytes(8)), + 'domain' => 'domain-' . random_int(10000, 99999), + 'path' => '/' . uniqid() . '/' . $i, + ]; + $list[] = $row; + $stmt->bindValue(1, $row['name']); + $stmt->bindValue(2, $row['domain']); + $stmt->bindValue(3, $row['path']); + $stmt->execute(); + } + + foreach ($list as $rs) { + Co\go(function () use ($rs) { + $pdo = new PDO(ODBC_DSN); + $statement = $pdo->query('select name, domain, path from ckl where path = "' . $rs['path'] . '" limit 1'); + Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs); + }); + } +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/race.phpt b/tests/swoole_pdo_odbc/race.phpt new file mode 100644 index 00000000000..79cd255ff56 --- /dev/null +++ b/tests/swoole_pdo_odbc/race.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_odbc: race +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + try { + $statement->execute(); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); + } catch (\PDOException $e) { + $msg[] = $e->getMessage(); + } + }); + } + Assert::count($msg, 0); +}); + +Assert::greaterThanEq(microtime(true) - $begin, 2); +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/server_info.phpt b/tests/swoole_pdo_odbc/server_info.phpt new file mode 100644 index 00000000000..c5ddc01d9f8 --- /dev/null +++ b/tests/swoole_pdo_odbc/server_info.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_pdo_odbc: test hook pgsql +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_SERVER_INFO); + Assert::eq(strtolower($info), 'mysql'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/sleep.phpt b/tests/swoole_pdo_odbc/sleep.phpt new file mode 100644 index 00000000000..35776405cc9 --- /dev/null +++ b/tests/swoole_pdo_odbc/sleep.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_pdo_odbc: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + $statement->execute(); + Assert::eq($sleep_count, N); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/transaction.phpt b/tests/swoole_pdo_odbc/transaction.phpt new file mode 100644 index 00000000000..93f92c556cd --- /dev/null +++ b/tests/swoole_pdo_odbc/transaction.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_pdo_odbc: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)'); + $row = [ + 'name' => base64_encode(random_bytes(8)), + 'domain' => 'domain-' . random_int(10000, 99999), + 'path' => '/' . uniqid() . '/' . 0, + ]; + $stmt->bindValue(1, $row['name']); + $stmt->bindValue(2, $row['domain']); + $stmt->bindValue(3, $row['path']); + $stmt->execute(); + }; + + $countTable = function ($pdo) { + return $pdo->query('select count(*) c from ckl')->fetch(PDO::FETCH_ASSOC)['c']; + }; + + $insertToTable($pdo); + var_dump('insert'); + + $c1 = $countTable($pdo); + + $pdo->beginTransaction(); + $insertToTable($pdo); + $pdo->rollBack(); + var_dump('rollback'); + + Assert::eq($countTable($pdo), $c1); + + $pdo->beginTransaction(); + $insertToTable($pdo); + $pdo->commit(); + var_dump('commit'); + + Assert::eq($countTable($pdo), $c1 + 1); +}); + +echo "Done\n"; +?> +--EXPECTF-- +string(6) "insert" +string(8) "rollback" +string(6) "commit" +Done diff --git a/tests/swoole_pdo_oracle/bug41996.phpt b/tests/swoole_pdo_oracle/bug41996.phpt new file mode 100644 index 00000000000..dd77f157c4a --- /dev/null +++ b/tests/swoole_pdo_oracle/bug41996.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI Bug #41996 (Problem accessing Oracle ROWID) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $stmt = $db->prepare('SELECT rowid FROM dual'); + $stmt->execute(); + $row = $stmt->fetch(); + var_dump(strlen($row[0]) > 0); +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt new file mode 100644 index 00000000000..4e61b2c6f76 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI Bug #44301 (Segfault when an exception is thrown on persistent connections) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_PERSISTENT, true); + try { + $stmt = $db->prepare('SELECT * FROM no_table'); + $stmt->execute(); + } catch (PDOException $e) { + print $e->getMessage(); + } + $db = null; +}); +?> +--EXPECTF-- +SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view "SYSTEM"."NO_TABLE" does not exist +Help: %s + (%s:%d) diff --git a/tests/swoole_pdo_oracle/bug46274.phpt b/tests/swoole_pdo_oracle/bug46274.phpt new file mode 100644 index 00000000000..0442d9571e0 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug46274.phpt @@ -0,0 +1,75 @@ +--TEST-- +swoole_pdo_oracle:ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + try { + $db->exec("DROP TABLE test_one_blob"); + } catch (Exception $e) { + } + + $db->beginTransaction(); + + $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + + $stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + + $data = 'foo'; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $data = ''; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $res = $db->query("SELECT blob1 from test_one_blob"); + // Resource + var_dump($res->fetch()); + + // Empty string + var_dump($res->fetch()); + + $db->exec("DROP TABLE test_one_blob"); +}); +?> +--EXPECT-- +array(2) { + ["blob1"]=> + string(3) "foo" + [0]=> + string(3) "foo" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} diff --git a/tests/swoole_pdo_oracle/bug46274_2.phpt b/tests/swoole_pdo_oracle/bug46274_2.phpt new file mode 100644 index 00000000000..fb6ff2cfd56 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug46274_2.phpt @@ -0,0 +1,80 @@ +--TEST-- +swoole_pdo_oracle:ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + try { + $db->exec("DROP TABLE test_one_blob"); + } catch (Exception $e) { + } + + $db->beginTransaction(); + + $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + + $stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + + $data = 'foo'; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $data = ''; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $res = $db->query("SELECT blob1 from test_one_blob"); + // Resource + var_dump($row = $res->fetch()); + var_dump(fread($row[0], 1024)); + fclose($row[0]); + + // Empty string + var_dump($row = $res->fetch()); + var_dump(fread($row[0], 1024)); + fclose($row[0]); + + $db->exec("DROP TABLE test_one_blob"); +}); +?> +--EXPECTF-- +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(3) "foo" +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(0) "" diff --git a/tests/swoole_pdo_oracle/bug54379.phpt b/tests/swoole_pdo_oracle/bug54379.phpt new file mode 100644 index 00000000000..e7ce939d6aa --- /dev/null +++ b/tests/swoole_pdo_oracle/bug54379.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_oracle: UTF-8 output gets truncated) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + try { + $db->exec("DROP TABLE test"); + } catch (Exception $e) { + } + $db->exec("CREATE TABLE test (col1 NVARCHAR2(20))"); + $db->exec("INSERT INTO test VALUES('12345678901234567890')"); + $db->exec("INSERT INTO test VALUES('あいうえおかきくけこさしすせそたちつてと')"); + $stmt = $db->prepare("SELECT * FROM test"); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $db->exec("DROP TABLE test"); +}); +?> +--EXPECT-- +array(2) { + [0]=> + array(1) { + ["col1"]=> + string(20) "12345678901234567890" + } + [1]=> + array(1) { + ["col1"]=> + string(60) "あいうえおかきくけこさしすせそたちつてと" + } +} diff --git a/tests/swoole_pdo_oracle/bug57702.phpt b/tests/swoole_pdo_oracle/bug57702.phpt new file mode 100644 index 00000000000..f02a5204d67 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug57702.phpt @@ -0,0 +1,190 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI Bug #57702 (Multi-row BLOB fetches) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + // Note the PDO test setup sets PDO::ATTR_STRINGIFY_FETCHES to true + // (and sets PDO::ATTR_CASE to PDO::CASE_LOWER) + + $query = "begin execute immediate 'drop table bug57702'; exception when others then if sqlcode <> -942 then raise; end if; end;"; + $stmt = $db->prepare($query); + $stmt->execute(); + + $query = "create table bug57702 (id number, data1 blob, data2 blob)"; + $stmt = $db->prepare($query); + $stmt->execute(); + + function do_insert($db, $id, $data1, $data2) + { + $db->beginTransaction(); + $stmt = $db->prepare("insert into bug57702 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); + } + + do_insert($db, 1, "row 1 col 1", "row 1 col 2"); + do_insert($db, 2, "row 2 col 1", "row 2 col 2"); + + //////////////////// + + echo "First Query\n"; + + // Fetch it back + $stmt = $db->prepare('select data1, data2 from bug57702 order by id'); + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row['data1']); + var_dump($row['data2']); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row['data1']); + var_dump($row['data2']); + + //////////////////// + + echo "\nSecond Query\n"; + + foreach($db->query("select data1 as d1, data2 as d2 from bug57702 order by id") as $row) { + var_dump($row['d1']); + var_dump($row['d2']); + } + + //////////////////// + + echo "\nThird Query\n"; + + $stmt = $db->prepare('select data1 as d3_1, data2 as d3_2 from bug57702 order by id'); + + $rs = $stmt->execute(); + $stmt->bindColumn('d3_1' , $clob1, PDO::PARAM_LOB); + $stmt->bindColumn('d3_2' , $clob2, PDO::PARAM_LOB); + + while ($stmt->fetch(PDO::FETCH_BOUND)) { + var_dump($clob1); + var_dump($clob2); + } + + //////////////////// + + echo "\nFourth Query\n"; + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; + } + + for ($i = 0; $i < count($a); $i++) { + var_dump($a[$i][0]); + var_dump($a[$i][1]); + } + + //////////////////// + + echo "\nFifth Query\n"; + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + // Since each column only has one lob descriptor, the last row is + // shown twice because the lob descriptor for each column is reused in + // the stream + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; + } + + for ($i = 0; $i < count($a); $i++) { + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); + } + + //////////////////// + + echo "\nSixth Query\n"; + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); + $i++; + } + + // Cleanup + $query = "drop table bug57702"; + $stmt = $db->prepare($query); + $stmt->execute(); + + print "done\n"; + +}); +?> +--EXPECTF-- +First Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Second Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Third Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Fourth Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Fifth Query +string(11) "row 2 col 1" +string(11) "row 2 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Sixth Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done diff --git a/tests/swoole_pdo_oracle/bug60994.phpt b/tests/swoole_pdo_oracle/bug60994.phpt new file mode 100644 index 00000000000..eecdcd9fdb4 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug60994.phpt @@ -0,0 +1,139 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI Bug #60994 (Reading a multibyte CLOB caps at 8192 characters) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + $dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB, data2 NCLOB)'); + + $id = null; + $insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data, data2) VALUES (:id, :data, :data2)'); + $insert->bindParam(':id', $id, \PDO::PARAM_STR); + $select = $dbh->prepare("SELECT data, data2 FROM pdo_oci_bug60994 WHERE id = :id"); + + + echo PHP_EOL, 'Test 1: j', PHP_EOL; + $string1 = 'abc' . str_repeat('j', 8187) . 'xyz'; // 8193 chars total works fine here (even 1 million works fine, subject to memory_limit) + $id = 1; + $insert->bindParam(':data', $string1, \PDO::PARAM_STR, strlen($string1)); // length in bytes + $insert->bindParam(':data2', $string1, \PDO::PARAM_STR, strlen($string1)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream1 = stream_get_contents($row['DATA']); + $start1 = mb_substr($stream1, 0, 10); + $ending1 = mb_substr($stream1, -10); + echo 'size of string1 is ', strlen($string1), ' bytes, ', mb_strlen($string1), ' chars.', PHP_EOL; + echo 'size of stream1 is ', strlen($stream1), ' bytes, ', mb_strlen($stream1), ' chars.', PHP_EOL; + echo 'beg of stream1 is ', $start1, PHP_EOL; + echo 'end of stream1 is ', $ending1, PHP_EOL; + if ($string1 != $stream1 || $stream1 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream1', PHP_EOL; + } + + echo PHP_EOL, 'Test 2: £', PHP_EOL; + $string2 = 'abc' . str_repeat('£', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 2; + $insert->bindParam(':data', $string2, \PDO::PARAM_STR, strlen($string2)); // length in bytes + $insert->bindParam(':data2', $string2, \PDO::PARAM_STR, strlen($string2)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream2 = stream_get_contents($row['DATA']); + $start2 = mb_substr($stream2, 0, 10); + $ending2 = mb_substr($stream2, -10); + echo 'size of string2 is ', strlen($string2), ' bytes, ', mb_strlen($string2), ' chars.', PHP_EOL; + echo 'size of stream2 is ', strlen($stream2), ' bytes, ', mb_strlen($stream2), ' chars.', PHP_EOL; + echo 'beg of stream2 is ', $start2, PHP_EOL; + echo 'end of stream2 is ', $ending2, PHP_EOL; + if ($string2 != $stream2 || $stream2 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream2', PHP_EOL; + } + + echo PHP_EOL, 'Test 3: Җ', PHP_EOL; + $string3 = 'abc' . str_repeat('Җ', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 3; + $insert->bindParam(':data', $string3, \PDO::PARAM_STR, strlen($string3)); // length in bytes + $insert->bindParam(':data2', $string3, \PDO::PARAM_STR, strlen($string3)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream3 = stream_get_contents($row['DATA']); + $start3 = mb_substr($stream3, 0, 10); + $ending3 = mb_substr($stream3, -10); + echo 'size of string3 is ', strlen($string3), ' bytes, ', mb_strlen($string3), ' chars.', PHP_EOL; + echo 'size of stream3 is ', strlen($stream3), ' bytes, ', mb_strlen($stream3), ' chars.', PHP_EOL; + echo 'beg of stream3 is ', $start3, PHP_EOL; + echo 'end of stream3 is ', $ending3, PHP_EOL; + if ($string3 != $stream3 || $stream3 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream3', PHP_EOL; + } + + echo PHP_EOL, 'Test 4: の', PHP_EOL; + $string4 = 'abc' . str_repeat('の', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 4; + $insert->bindParam(':data', $string4, \PDO::PARAM_STR, strlen($string4)); // length in bytes + $insert->bindParam(':data2', $string4, \PDO::PARAM_STR, strlen($string4)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream4 = stream_get_contents($row['DATA']); + $start4 = mb_substr($stream4, 0, 10); + $ending4 = mb_substr($stream4, -10); + echo 'size of string4 is ', strlen($string4), ' bytes, ', mb_strlen($string4), ' chars.', PHP_EOL; + echo 'size of stream4 is ', strlen($stream4), ' bytes, ', mb_strlen($stream4), ' chars.', PHP_EOL; + echo 'beg of stream4 is ', $start4, PHP_EOL; + echo 'end of stream4 is ', $ending4, PHP_EOL; + if ($string4 != $stream4 || $stream4 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream4', PHP_EOL; + } + $dbh->exec('DROP TABLE pdo_oci_bug60994'); +}); + +?> +--EXPECT-- +Test 1: j +size of string1 is 8193 bytes, 8193 chars. +size of stream1 is 8193 bytes, 8193 chars. +beg of stream1 is abcjjjjjjj +end of stream1 is jjjjjjjxyz + +Test 2: £ +size of string2 is 16380 bytes, 8193 chars. +size of stream2 is 16380 bytes, 8193 chars. +beg of stream2 is abc£££££££ +end of stream2 is £££££££xyz + +Test 3: Җ +size of string3 is 16380 bytes, 8193 chars. +size of stream3 is 16380 bytes, 8193 chars. +beg of stream3 is abcҖҖҖҖҖҖҖ +end of stream3 is ҖҖҖҖҖҖҖxyz + +Test 4: の +size of string4 is 24567 bytes, 8193 chars. +size of stream4 is 24567 bytes, 8193 chars. +beg of stream4 is abcののののののの +end of stream4 is のののののののxyz diff --git a/tests/swoole_pdo_oracle/bug_33707.phpt b/tests/swoole_pdo_oracle/bug_33707.phpt new file mode 100644 index 00000000000..6662bdd04d6 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug_33707.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI Bug #33707 (Errors in select statements not reported) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $rs = $db->query('select blah from a_table_that_does_not_exist'); + var_dump($rs); + var_dump($db->errorInfo()); +}); +?> +--EXPECTF-- +bool(false) +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(942) + [2]=> + string(%d) "OCIStmtExecute: ORA-00942: table or view "SYSTEM"."A_TABLE_THAT_DOES_NOT_EXIST" does not exist +Help: %s + (%s:%d)" +} diff --git a/tests/swoole_pdo_oracle/checkliveness.phpt b/tests/swoole_pdo_oracle/checkliveness.phpt new file mode 100644 index 00000000000..da2930e3a79 --- /dev/null +++ b/tests/swoole_pdo_oracle/checkliveness.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI checkliveness (code coverage) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true)); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(); + exit; + } + + // This triggers the call to check liveness + try { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true)); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(); + exit; + } + + $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + + try { + $stmt = $db->prepare('SELECT * FROM dual'); + $stmt->execute(); + $row = $stmt->fetch(); + var_dump($row); + } catch (PDOException $e) { + print $e->getMessage(); + } + + $db = null; +}); +?> +--EXPECT-- +array(2) { + ["DUMMY"]=> + string(1) "X" + [0]=> + string(1) "X" +} diff --git a/tests/swoole_pdo_oracle/coroutint.phpt b/tests/swoole_pdo_oracle/coroutint.phpt new file mode 100644 index 00000000000..fc043e28cb1 --- /dev/null +++ b/tests/swoole_pdo_oracle/coroutint.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI coroutine +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec("create table test (id int)"); + for($i = 0; $i < 10; $i++) { + go(function () use($db, $i){ + $stmt = $db->prepare("insert into test values (?)"); + $stmt->execute([$i]); + $stmt = $db->prepare("select id from test where id = ?"); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } + sleep(1); + $db->exec("drop table test"); +}); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_oracle/oci_success_with_info.phpt b/tests/swoole_pdo_oracle/oci_success_with_info.phpt new file mode 100644 index 00000000000..435e3c258f5 --- /dev/null +++ b/tests/swoole_pdo_oracle/oci_success_with_info.phpt @@ -0,0 +1,126 @@ +--TEST-- +swoole_pdo_oracle: Handling OCI_SUCCESS_WITH_INFO +--SKIPIF-- + +--FILE-- +exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP PROFILE BUG77120_PROFILE CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2380 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function dropUser(PDO $conn): void { + $conn->exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP USER BUG77120_USER CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -1918 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function triggerCompilationError(PDO $conn): void { + $conn->exec(<<<'SQL' +CREATE OR REPLACE FUNCTION BUG77120(INT A) RETURN INT +AS +BEGIN + RETURN 0; +END; +SQL + ); +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $conn = connectAsAdmin(); + + dropUser($conn); + dropProfile($conn); + + $password = bin2hex(random_bytes(8)); + + $conn->exec('CREATE PROFILE BUG77120_PROFILE LIMIT PASSWORD_LIFE_TIME 1/86400 PASSWORD_GRACE_TIME 1'); + $conn->exec('CREATE USER BUG77120_USER IDENTIFIED BY "' . $password . '" PROFILE BUG77120_PROFILE'); + $conn->exec('GRANT CREATE SESSION TO BUG77120_USER'); + + // let the password expire + sleep(3); // 2 seconds is causing random test failures + + $conn = connectAsUser('BUG77120_USER', $password); + var_dump($conn->errorInfo()); + + $conn = connectAsAdmin(); + dropUser($conn); + dropProfile($conn); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + triggerCompilationError($conn); + var_dump($conn->errorInfo()); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + triggerCompilationError($conn); + var_dump($conn->errorInfo()); +}); + +?> +--EXPECTF-- +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(28002) + [2]=> + string(%d) "OCISessionBegin: OCI_SUCCESS_WITH_INFO: ORA-28002: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +} diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt new file mode 100644 index 00000000000..bd3e6d113e9 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_pdo_oracle: Setting session action +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $query = 'select action from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION NOT SET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, "some action")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION SET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION RESET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION NULLED: '; + var_dump($row['action']); + + echo "Done\n"; +}); +?> +--EXPECT-- +ACTION NOT SET: NULL +bool(true) +ACTION SET: string(11) "some action" +bool(true) +ACTION RESET: string(15) "something else!" +bool(true) +ACTION NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt new file mode 100644 index 00000000000..a403e53b778 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_pdo_oracle: Basic autocommit functionality +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->exec("drop table pdo_ac_tab"); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + print "PDO::ATTR_AUTOCOMMIT: Default: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Change setting to false - "; + + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Change setting back to true - "; + + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + // Use 2nd connection to check that autocommit does commit + + echo "Insert data\n"; + $dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + + $dbh2 = PdoOracleTest::create(); + + echo "Second connection should be able to see committed data\n"; + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; + } + + $dbh->exec("drop table pdo_ac_tab"); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_AUTOCOMMIT: Default: bool(true) +Change setting to false - PDO::ATTR_AUTOCOMMIT: bool(false) +Change setting back to true - PDO::ATTR_AUTOCOMMIT: bool(true) +Insert data +Second connection should be able to see committed data +Data is: some data +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt new file mode 100644 index 00000000000..4af96053e69 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt @@ -0,0 +1,132 @@ +--TEST-- +swoole_pdo_oracle: beginTransaction and native transactions +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->exec("drop table pdo_ac_tab"); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $dbh->exec("create table pdo_ac_tab (col1 varchar2(25))"); + + echo "Test 1 Check beginTransaction insertion\n"; + + $dbh->beginTransaction(); + try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 2')"); + $dbh->commit(); + } + catch (PDOException $e) { + echo "Caught unexpected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); + } + + echo "Test 2 Cause an exception and test beginTransaction rollback\n"; + + $dbh->beginTransaction(); + try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data that is too long to fit and will barf')"); + $dbh->commit(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); + } + + echo "Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction\n"; + + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 3')"); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + try { + $dbh->rollback(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + } + + echo "Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction\n"; + + $dbh->beginTransaction(); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 4')"); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + try { + $dbh->rollback(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + } + + echo "Test 5 Handle transactions ourselves\n"; + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #2')"); + $dbh->exec("rollback"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 5')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 6')"); + + $dbh->exec("commit"); + + // Open new connection to really verify what was inserted + + $dbh2 = PdoOracleTest::create(); + + echo "Query Results are:\n"; + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1 Check beginTransaction insertion +Test 2 Cause an exception and test beginTransaction rollback +Caught expected exception at line %d +SQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s +Help: %s + (%s:%d) +Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(true) +Caught expected exception at line %d +There is no active transaction +Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(false) +Caught expected exception at line %d +There is no active transaction +Test 5 Handle transactions ourselves +PDO::ATTR_AUTOCOMMIT: bool(false) +Query Results are: +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt new file mode 100644 index 00000000000..a39c3a6d62b --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_pdo_oracle: closing a connection in non-autocommit mode commits data +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + + // Check connection can be created with AUTOCOMMIT off + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + $dbh->exec("drop table pdo_ac_tab"); + + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Insert data\n"; + + $dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); + + $dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + + $dbh = null; // close first connection + + echo "Second connection should be able to see committed data\n"; + $dbh2 = PdoOracleTest::create(); + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; + } + + $dbh2->exec("drop table pdo_ac_tab"); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_AUTOCOMMIT: bool(false) +Insert data +Second connection should be able to see committed data +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt new file mode 100644 index 00000000000..94a337364e6 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_pdo_oracle: Setting and using call timeout +--SKIPIF-- + +getAttribute(PDO::ATTR_CLIENT_VERSION), $matches); +if (!(isset($matches[0]) && $matches[0] >= 18)) { + die("skip works only with Oracle 18c or greater version of Oracle client libraries"); +} + +?> +--FILE-- +prepare("begin dbms_lock.sleep(:t); end;"); + + if (!$stmt) { + $error = $dbh->errorInfo(); + echo "Prepare error was ", $error[2], "\n"; + return; + } + $stmt->bindParam(":t", $t, PDO::PARAM_INT); + + $r = $stmt->execute(); + if ($r) { + echo "Execute succeeded\n"; + } else { + $error = $dbh->errorInfo(); + echo "Execute error was ", $error[2], "\n"; + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + echo "Test 1\n"; + + $dbh->setAttribute(PDO::OCI_ATTR_CALL_TIMEOUT, 4000); // milliseconds + + echo "call timeout:\n"; + var_dump($dbh->getAttribute(PDO::OCI_ATTR_CALL_TIMEOUT)); + + $r = mysleep($dbh, 8); // seconds +}); +?> +===DONE=== + +--EXPECTF-- +Test 1 +call timeout: +int(4000) +Execute error was OCIStmtExecute: ORA-%r(03136|03156)%r: %s + (%s:%d) +===DONE=== diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt new file mode 100644 index 00000000000..8947f97ea27 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt @@ -0,0 +1,85 @@ +--TEST-- +swoole_pdo_oracle: Column Case +--SKIPIF-- + + +--FILE-- +getAttribute(PDO::ATTR_CASE)); + $s = $dbh->prepare("select dummy from dual"); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +function do_query2($dbh, $mode) +{ + echo "Mode desired is $mode\n"; + $s = $dbh->prepare("select dummy from dual", array(PDO::ATTR_CASE, $mode)); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + echo "Test 1 - Force column names to lower case\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + do_query1($dbh); + + echo "Test 2 - Leave column names as returned by the database driver\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + do_query1($dbh); + + echo "Test 3 - Force column names to upper case\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + do_query1($dbh); + + echo "Test 4 - Setting on statement has no effect. Attempt lower case but get upper\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); // reset + do_query2($dbh, PDO::CASE_LOWER); + + echo "Done\n"; +}); +?> +--EXPECT-- +Test 1 - Force column names to lower case +int(2) +array(1) { + ["dummy"]=> + string(1) "X" +} +Test 2 - Leave column names as returned by the database driver +int(0) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 3 - Force column names to upper case +int(1) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 4 - Setting on statement has no effect. Attempt lower case but get upper +Mode desired is 2 +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt new file mode 100644 index 00000000000..4cc54a032bb --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_pdo_oracle: Client version +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "ATTR_CLIENT_VERSION: "; + $cv = $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION); + var_dump($cv); + + $s = explode(".", $cv); + if (count($s) > 1 && (($s[0] == 10 && $s[1] >= 2) || $s[0] >= 11)) { + if (count($s) != 5) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } + } else { + if (count($s) != 2) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +ATTR_CLIENT_VERSION: string(%d) "%d.%s" +Version OK, so far as can be portably checked +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt new file mode 100644 index 00000000000..a9004e4cc86 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_pdo_oracle: Setting session client identifier +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $query = 'select client_identifier from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + + $dbh = PdoOracleTest::create(); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER NOT SET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, "some client identifier")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER SET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER RESET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER NULLED: '; + var_dump($row['client_identifier']); + + echo "Done\n"; +}); +?> +--EXPECT-- +CLIENT_IDENTIFIER NOT SET: NULL +bool(true) +CLIENT_IDENTIFIER SET: string(22) "some client identifier" +bool(true) +CLIENT_IDENTIFIER RESET: string(15) "something else!" +bool(true) +CLIENT_IDENTIFIER NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt new file mode 100644 index 00000000000..4e2b953a7ce --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_pdo_oracle: Setting session client info +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $query = 'select client_info from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + $dbh = PdoOracleTest::create(); + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO NOT SET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, "some client info")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO SET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO RESET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO NULLED: '; + var_dump($row['client_info']); + + echo "Done\n"; +}); +?> +--EXPECT-- +CLIENT_INFO NOT SET: NULL +bool(true) +CLIENT_INFO SET: string(16) "some client info" +bool(true) +CLIENT_INFO RESET: string(15) "something else!" +bool(true) +CLIENT_INFO NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt new file mode 100644 index 00000000000..2444222c4a2 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_pdo_oracle: verify driver name +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + var_dump($dbh->getAttribute(PDO::ATTR_DRIVER_NAME)); + echo "Done\n"; +}); +?> +--EXPECT-- +string(3) "oci" +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt new file mode 100644 index 00000000000..1d4a4218690 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_pdo_oracle: Setting session module +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); + +run(function() { + $query = 'select module from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + + $dbh = PdoOracleTest::create(); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, "some module")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE SET: '; + var_dump($row['module']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE RESET: '; + var_dump($row['module']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE NULLED: '; + var_dump($row['module']); + + echo "Done\n"; +}); +?> +--EXPECT-- +bool(true) +MODULE SET: string(11) "some module" +bool(true) +MODULE RESET: string(15) "something else!" +bool(true) +MODULE NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt new file mode 100644 index 00000000000..37e40340ac6 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_pdo_oracle: Oracle Nulls +--SKIPIF-- + + +--FILE-- +getAttribute(PDO::ATTR_ORACLE_NULLS)); + $s = $dbh->prepare("select '' as myempty, null as mynull from dual"); + $s->execute(); + while ($r = $s->fetch()) { + var_dump($r[0]); + var_dump($r[1]); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + print "PDO::ATTR_ORACLE_NULLS: Default: "; + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL); // No conversion. + + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); // Empty string is converted to NULL. + + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); // NULL is converted to an empty string. + + do_query($dbh); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_ORACLE_NULLS: Default: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: int(1) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: int(2) +string(0) "" +string(0) "" +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt new file mode 100644 index 00000000000..6d45fe3949b --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt @@ -0,0 +1,80 @@ +--TEST-- +swoole_pdo_oracle: Set prefetch on connection +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "Test connect\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, 101); + + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set 102\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, 102); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set -1: (Uses 0)\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, -1); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set PHP_INT_MAX: (Uses default)\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, PHP_INT_MAX); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECT-- +Test connect +101 +X +Test set 102 +102 +X +Test set -1: (Uses 0) +0 +X +Test set PHP_INT_MAX: (Uses default) +100 +X +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt new file mode 100644 index 00000000000..2cf9eee1f63 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_pdo_oracle: prefetch on statements +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 101)); + + echo "Test 1: Can't set prefetch after prepare\n"; + var_dump($s->setAttribute(PDO::ATTR_PREFETCH, 102)); + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test 2: Turn off prefetching\n"; + $s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 0)); + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1: Can't set prefetch after prepare + +Fatal error: Uncaught PDOException: SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes in %s:%d +Stack trace: +#0 %s(%d): PDOStatement->setAttribute(1, 102) +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt new file mode 100644 index 00000000000..9ad34f30fa1 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_pdo_oracle: Server version and info +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "Test 1\n"; + echo "ATTR_SERVER_VERSION: "; + var_dump($dbh->getAttribute(PDO::ATTR_SERVER_VERSION)); + + echo "Test 2\n"; + echo "ATTR_SERVER_INFO\n"; + $si = $dbh->getAttribute(PDO::ATTR_SERVER_INFO); + $pos = strpos($si, "Oracle"); + if ($pos === 0) { + echo "Found 'Oracle' at position $pos as expected\n"; + } else { + echo "Unexpected result. Server info was:\n"; + var_dump($si); + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1 +ATTR_SERVER_VERSION: string(%d) "%d.%d.%d.%d.%d" +Test 2 +ATTR_SERVER_INFO +Found 'Oracle' at position 0 as expected +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt b/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt new file mode 100644 index 00000000000..033586d5946 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI specific class constants +--SKIPIF-- + + +--FILE-- + true, + 'OCI_ATTR_ACTION' => true, + 'OCI_ATTR_CLIENT_IDENTIFIER' => true, + 'OCI_ATTR_MODULE' => true, + 'OCI_ATTR_CALL_TIMEOUT' => true, +]; + +$ref = new ReflectionClass('PDO'); +$constants = $ref->getConstants(); +$values = []; + +foreach ($constants as $name => $value) { + if (substr($name, 0, 8) == 'OCI_ATTR') { + if (!isset($values[$value])) { + $values[$value] = [$name]; + } else { + $values[$value][] = $name; + } + + if (isset($expected[$name])) { + unset($expected[$name]); + unset($constants[$name]); + } + + } else { + unset($constants[$name]); + } +} + +if (!empty($constants)) { + printf("[001] Dumping list of unexpected constants\n"); + var_dump($constants); +} + +if (!empty($expected)) { + printf("[002] Dumping list of missing constants\n"); + var_dump($expected); +} + +if (!empty($values)) { + foreach ($values as $value => $constants) { + if (count($constants) > 1) { + printf("[003] Several constants share the same value '%s'\n", $value); + var_dump($constants); + } + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt b/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt new file mode 100644 index 00000000000..64927463e68 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_pdo_oracle:debugDumpParams() truncates query) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); +$stmt = $db->query(" +SELECT ' + 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). + This is a debug function, which dump directly the data on the normal output. + Tip: + 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). + 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. +' FROM DUAL +"); +var_dump($stmt->debugDumpParams()); +}); +?> +--EXPECTF-- +SQL: [%d] +SELECT ' + 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). + This is a debug function, which dump directly the data on the normal output. + Tip: + 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). + 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. +' FROM DUAL + +Params: 0 +NULL diff --git a/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt new file mode 100644 index 00000000000..2dc11a2fb1a --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_pdo_oracle: check fread() EOF +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + // Initialization + $stmtarray = array( + "begin execute immediate 'drop table pdo_oci_fread_tab'; exception when others then null; end;", + "create table pdo_oci_fread_tab (id number, data clob)", + "declare + lob1 clob := 'abc' || lpad('j',4020,'j') || 'xyz'; + begin + insert into pdo_oci_fread_tab (id,data) values (1, lob1); + end;" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } + + echo "Test 1\n"; + + $s = $dbh->query("select data from pdo_oci_fread_tab where id = 1"); + $r = $s->fetch(); + $sh = $r['data']; + + while (1) { + $buffer = fread($sh,1024); + if (!$buffer) { + break; + } + echo '*'.$buffer.'*'; + } + echo "\n"; + fclose($sh); + + // Clean up + + $stmtarray = array( + "drop table pdo_oci_fread_tab" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } +}); +?> +--EXPECT-- +Test 1 +*abcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjxyz* diff --git a/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt b/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt new file mode 100644 index 00000000000..0fa5df9794f --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_oracle: phpinfo() output +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + ob_start(); + phpinfo(); + $tmp = ob_get_contents(); + ob_end_clean(); + + $reg = 'coroutine_oracle => enabled'; + if (!preg_match("/$reg/", $tmp)) { + printf("[001] Cannot find OCI PDO driver line in phpinfo() output\n"); + } + + print "done!"; +}); +?> +--EXPECT-- +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt b/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt new file mode 100644 index 00000000000..fe6b359a0fe --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt @@ -0,0 +1,156 @@ +--TEST-- +swoole_pdo_oracle: Test PDO->quote() for PDO_OCI +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->query("create table poq_tab (t varchar2(100))"); + $stmt = $db->prepare('select * from poq_tab'); + + // The intent is that the fetched data be identical to the unquoted string. + // Remember!: use bind variables instead of PDO->quote() + + $a = array("", "a", "ab", "abc", "ab'cd", "a\b\n", "'", "''", "a'", "'z", "a''b", '"'); + foreach ($a as $u) { + $q = $db->quote($u); + echo "Unquoted : "; + var_dump($u); + echo "Quoted : "; + var_dump($q); + + $db->exec("delete from poq_tab"); + + $db->query("insert into poq_tab (t) values($q)"); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } + + echo "Done\n"; + $db->exec("drop table poq_tab"); +}); +?> +--EXPECT-- +Unquoted : string(0) "" +Quoted : string(2) "''" +array(1) { + [0]=> + array(1) { + ["t"]=> + NULL + } +} +Unquoted : string(1) "a" +Quoted : string(3) "'a'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "a" + } +} +Unquoted : string(2) "ab" +Quoted : string(4) "'ab'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "ab" + } +} +Unquoted : string(3) "abc" +Quoted : string(5) "'abc'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(3) "abc" + } +} +Unquoted : string(5) "ab'cd" +Quoted : string(8) "'ab''cd'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(5) "ab'cd" + } +} +Unquoted : string(4) "a\b +" +Quoted : string(6) "'a\b +'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a\b +" + } +} +Unquoted : string(1) "'" +Quoted : string(4) "''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "'" + } +} +Unquoted : string(2) "''" +Quoted : string(6) "''''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "''" + } +} +Unquoted : string(2) "a'" +Quoted : string(5) "'a'''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "a'" + } +} +Unquoted : string(2) "'z" +Quoted : string(5) "'''z'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "'z" + } +} +Unquoted : string(4) "a''b" +Quoted : string(8) "'a''''b'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a''b" + } +} +Unquoted : string(1) """ +Quoted : string(3) "'"'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) """ + } +} +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt b/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt new file mode 100644 index 00000000000..0f2563a9a14 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt @@ -0,0 +1,338 @@ +--TEST-- +swoole_pdo_oracle: PDOStatement->getColumnMeta +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $db = PdoOracleTest::create(); + $db->exec(<<exec("CREATE TABLE test(id INT)"); + + $db->beginTransaction(); + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + + echo "Test 1. calling function with invalid parameters\n"; + + // execute() has not been called yet + // NOTE: no warning + $tmp = $stmt->getColumnMeta(0); + printf(" 1.1 Expecting false got %s\n", var_export($tmp, true)); + + echo(" 1.2 "); + $stmt->execute(); + // PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given in + try { + $tmp = $stmt->getColumnMeta(); + } catch (ArgumentCountError $e) { + if (false !== $tmp) { + printf("[1.2] Expecting false got %s\n", var_export($tmp, true)); + } + echo $e->getMessage(), "\n"; + } + + // invalid offset + echo " 1.3 "; + try { + $tmp = $stmt->getColumnMeta(-1); + } catch (ValueError $e) { + if (false !== $tmp) { + printf("[1.3] Expecting false got %s\n", var_export($tmp, true)); + } + echo $e->getMessage(), "\n"; + } + + // PDOStatement::getColumnMeta(): Argument #1 must be of type int, array given in + echo " 1.4 "; + try { + $tmp = $stmt->getColumnMeta(array()); + } catch (TypeError $e) { + if (false !== $tmp) + printf("[1.4] Expecting false got %s\n", var_export($tmp, true)); + echo $e->getMessage(), "\n"; + } + + // PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given in + echo " 1.5 "; + try { + $tmp = $stmt->getColumnMeta(1, 1); + } catch (ArgumentCountError $e) { + if (false !== $tmp) + printf("[1.5] Expecting false got %s\n", var_export($tmp, true)); + echo $e->getMessage(), "\n"; + } + + // invalid offset + $tmp = $stmt->getColumnMeta(1); + printf(" 1.6 Expecting false because of invalid offset got %s\n", var_export($tmp, true)); + + echo "Test 2. testing return values\n"; + echo "Test 2.1 testing array returned\n"; + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + $stmt->execute(); + $native = $stmt->getColumnMeta(0); + if (count($native) == 0) { + printf("[008] Meta data seems wrong, %s / %s\n", + var_export($native, true), var_export($emulated, true)); + } + + + function test_return($meta, $offset, $native_type, $pdo_type){ + if (empty($meta)) { + printf("[%03d + 2] getColumnMeta() failed, %d - %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + $elements = array('flags', 'scale', 'name', 'len', 'precision', 'pdo_type'); + foreach ($elements as $k => $element) + if (!isset($meta[$element])) { + printf("[%03d + 3] Element %s missing, %s\n", $offset, + $element, var_export($meta, true)); + return false; + } + + if (!is_null($native_type)) { + if (!isset($meta['native_type'])) { + printf("[%03d + 4] Element native_type missing, %s\n", $offset, + var_export($meta, true)); + return false; + } + + if (!is_array($native_type)) + $native_type = array($native_type); + + $found = false; + foreach ($native_type as $k => $type) { + if ($meta['native_type'] == $type) { + $found = true; + break; + } + } + + if (!$found) { + printf("[%03d + 5] Expecting native type %s, %s\n", $offset, + var_export($native_type, true), var_export($meta, true)); + return false; + } + } + + if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) { + printf("[%03d + 6] Expecting PDO type %s got %s (%s)\n", $offset, + $pdo_type, var_export($meta, true), var_export($meta['native_type'])); + return false; + } + + return true; + } + + + function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) { + + $db->exec(<<prepare($sql); + $stmt->execute(); + + if (!$db->exec(sprintf("INSERT INTO test(id, label) VALUES (1, '%s')", $value))) { + printf("[%03d] + 1] Insert failed, %d - %s\n", $offset, + $db->errorCode(), var_export($db->errorInfo(), true)); + return false; + } + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + $meta = $stmt->getColumnMeta(1); + return test_return($meta, $offset, $native_type, $pdo_type); + } + + echo "Test 2.2 testing numeric columns\n"; + + test_meta($db, 20, 'NUMBER' , 0 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 30, 'NUMBER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 40, 'INT' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 50, 'INTEGER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 60, 'NUMBER' , 256.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 70, 'NUMBER' , -8388608 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 80, 'NUMBER' , 2147483648 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 90, 'NUMBER' , 4294967295 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 100, 'DEC' , 1.01 , 'NUMBER' , PDO::PARAM_STR); + test_meta($db, 110, 'DECIMAL' , 1.01 , 'NUMBER' , PDO::PARAM_STR); + test_meta($db, 120, 'FLOAT' , 1.01 , 'FLOAT' , PDO::PARAM_STR); + test_meta($db, 130, 'DOUBLE PRECISION', 1.01 , 'FLOAT' , PDO::PARAM_STR); + test_meta($db, 140, 'BINARY_FLOAT' , 1.01 , 'BINARY_FLOAT' , PDO::PARAM_STR); + test_meta($db, 150, 'BINARY_DOUBLE' , 1.01 , 'BINARY_DOUBLE', PDO::PARAM_STR); + + echo "Test 2.3 testing temporal columns\n"; + + $db->exec("alter session set nls_date_format='YYYY-MM-DD'"); + test_meta($db, 160, 'DATE' , '2008-04-23' , 'DATE', PDO::PARAM_STR); + + echo "Test 2.4 testing string columns\n"; + + test_meta($db, 170, 'CHAR(1)' , 'a' , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 180, 'CHAR(10)' , '0123456789' , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 190, 'CHAR(255)' , str_repeat('z', 255) , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 200, 'VARCHAR(1)' , 'a' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 210, 'VARCHAR(10)' , '0123456789' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 220, 'VARCHAR(255)' , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 230, 'VARCHAR2(1)' , 'a' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 240, 'VARCHAR2(10)' , '0123456789' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 250, 'VARCHAR2(255)' , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR); + + test_meta($db, 260, 'NCHAR(1)' , 'a' , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 270, 'NCHAR(10)' , '0123456789' , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 280, 'NCHAR(255)' , str_repeat('z', 255) , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 290, 'NVARCHAR2(1)' , 'a' , 'NVARCHAR2', PDO::PARAM_STR); + test_meta($db, 300, 'NVARCHAR2(10)' , '0123456789' , 'NVARCHAR2', PDO::PARAM_STR); + test_meta($db, 310, 'NVARCHAR2(255)', str_repeat('z', 255) , 'NVARCHAR2', PDO::PARAM_STR); + + echo "Test 2.5 testing lobs columns\n"; + + test_meta($db, 320, 'CLOB' , str_repeat('b', 255) , 'CLOB' , PDO::PARAM_LOB); + test_meta($db, 330, 'BLOB' , str_repeat('b', 256) , 'BLOB' , PDO::PARAM_LOB); + test_meta($db, 340, 'NCLOB' , str_repeat('b', 255) , 'NCLOB' , PDO::PARAM_LOB); + + test_meta($db, 350, 'LONG' , str_repeat('b', 256) , 'LONG' , PDO::PARAM_STR); + test_meta($db, 360, 'LONG RAW' , str_repeat('b', 256) , 'LONG RAW', PDO::PARAM_STR); + test_meta($db, 370, 'RAW(256)' , str_repeat('b', 256) , 'RAW' , PDO::PARAM_STR); + + + $db->exec(<<query('SELECT count(*) FROM dual'); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 380, 'NUMBER', PDO::PARAM_STR); + $stmt = $db->query("SELECT TO_DATE('2008-04-23') FROM dual"); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 390, 'DATE', PDO::PARAM_STR); + $stmt = $db->query("SELECT TO_CHAR(542) FROM dual"); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 400, 'VARCHAR2', PDO::PARAM_STR); + + + echo "Test 2.7 testing flags returned\n"; + + $sql = sprintf('CREATE TABLE test(id INT NOT NULL, label INT NULL)'); + $stmt = $db->prepare($sql); + $stmt->execute(); + $db->exec('INSERT INTO test(id, label) VALUES (1, 1)'); + $stmt = $db->query('SELECT id, label FROM test'); + $meta = $stmt->getColumnMeta(0); + // verify the flags array contains a not_null flag and not nullable flags + if (!isset($meta['flags'])) { + printf("[1001] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + $found = true; + if ($flag == 'nullable') + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + if (!$found) + printf("[1002] Flags seem wrong %s\n", var_export($meta, true)); + } + $meta = $stmt->getColumnMeta(1); + // verify the flags array contains a nullable flag and not not_null flags + if (!isset($meta['flags'])) { + printf("[1003] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + printf("[1004] Flags seem wrong %s\n", var_export($meta, true)); + if ($flag == 'nullable') + $found = true; + } + if (!$found) + printf("[1005] Flags seem wrong %s\n", var_export($meta, true)); + } + + } catch (PDOException $e) { + // we should never get here, we use warnings, but never trust a system... + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); + } + + $db->exec(<< +--EXPECT-- +Preparations before the test +Test 1. calling function with invalid parameters + 1.1 Expecting false got false + 1.2 PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given + 1.3 PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0 + 1.4 PDOStatement::getColumnMeta(): Argument #1 ($column) must be of type int, array given + 1.5 PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given + 1.6 Expecting false because of invalid offset got false +Test 2. testing return values +Test 2.1 testing array returned +Test 2.2 testing numeric columns +Test 2.3 testing temporal columns +Test 2.4 testing string columns +Test 2.5 testing lobs columns +Test 2.6 testing function return +Test 2.7 testing flags returned +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt new file mode 100644 index 00000000000..5ebb28e1e09 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt @@ -0,0 +1,118 @@ +--TEST-- +swoole_pdo_oracle: stream_get_contents length & offset test +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + // Initialization + + $stmtarray = array( + "create table pdo_oci_stream_1_tab (id number, data clob)", + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } + + $dbh->exec(" + declare + lob1 clob := 'abc' || lpad('j',30000,'j') || 'xyz'; + begin + insert into pdo_oci_stream_1_tab (id,data) values (1, 'abcdefghijklmnopqrstuvwxyz'); + insert into pdo_oci_stream_1_tab (id,data) values (2, lob1); + end;"); + + echo "Test 1\n"; + + $s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 1"); + $s->execute(); + $r = $s->fetch(); + + // stream_get_contents ( resource $handle [, int $maxlength = -1 [, int $offset = -1 ]] ) + echo 'Read '.stream_get_contents($r['data'], 1, 1)."$\n"; // b + echo 'Read '.stream_get_contents($r['data'], 2, 1)."$\n"; // cd + echo 'Read '.stream_get_contents($r['data'], 2, 0)."$\n"; // ab + echo 'Read '.stream_get_contents($r['data'], 26, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 27, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 27, 1)."$\n"; // bcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 1, 20)."$\n"; // u + echo 'Read '.stream_get_contents($r['data'], 1, 25)."$\n"; // z + echo 'Read '.stream_get_contents($r['data'], 1, 26)."$\n"; // + echo 'Read '.stream_get_contents($r['data'], 1, 0)."$\n"; // a + + echo "\nTest 2\n"; + + $s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 2"); + $s->execute(); + $r = $s->fetch(); + + echo 'Read '.stream_get_contents($r['data'], 5, 0)."\n"; // abcjj + echo 'Read '.stream_get_contents($r['data'], 5, 2)."\n"; // cjjjj + echo 'Read '.stream_get_contents($r['data'], 6, 1)."\n"; // bcjjjj + echo 'Read '.strlen(stream_get_contents($r['data'], -1,0))."\n"; // 30006 + echo 'Read '.strlen(stream_get_contents($r['data'], 0,0))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 0,1))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 10,100))."\n"; // 10 + echo 'Read '.stream_get_contents($r['data'], 6, 30000)."\n"; // jjjxyz + echo 'Read '.stream_get_contents($r['data'], 7, 30000)."\n"; // jjjxyz + echo 'Read '.strlen(stream_get_contents($r['data']))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 0))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], -1))."\n"; // 0 + echo 'Read '.stream_get_contents($r['data'], -1, 30000)."\n"; // jjjxyz + + // Clean up + + $stmtarray = array( + "drop table pdo_oci_stream_1_tab" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } +}); +?> +--EXPECT-- +Test 1 +Read b$ +Read cd$ +Read ab$ +Read abcdefghijklmnopqrstuvwxyz$ +Read abcdefghijklmnopqrstuvwxyz$ +Read bcdefghijklmnopqrstuvwxyz$ +Read u$ +Read z$ +Read $ +Read a$ + +Test 2 +Read abcjj +Read cjjjj +Read bcjjjj +Read 30006 +Read 0 +Read 0 +Read 10 +Read jjjxyz +Read jjjxyz +Read 0 +Read 0 +Read 0 +Read jjjxyz diff --git a/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt new file mode 100644 index 00000000000..be3b350064c --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt @@ -0,0 +1,128 @@ +--TEST-- +swoole_pdo_oracle: Insert and fetch 1K records from a table that contains 1 number and 2 LOB columns (stress test) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $query = "begin execute immediate 'drop table pdo_oci_stream_2'; exception when others then if sqlcode <> -942 then raise; end if; end;"; + $stmt = $db->prepare($query); + $stmt->execute(); + + $query = "create table pdo_oci_stream_2 (id number, data1 blob, data2 blob)"; + $stmt = $db->prepare($query); + $stmt->execute(); + + function do_insert($db, $id, $data1, $data2) + { + $db->beginTransaction(); + $stmt = $db->prepare("insert into pdo_oci_stream_2 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); + } + + $a1 = str_repeat('a', 4086); + $a2 = str_repeat('b', 4087); + $a3 = str_repeat('c', 4088); + $a4 = str_repeat('d', 4089); + $a5 = str_repeat('e', 4090); + $a6 = str_repeat('f', 4091); + $a7 = str_repeat('g', 4092); + $a8 = str_repeat('h', 4093); + $a9 = str_repeat('i', 4094); + $a10 = str_repeat('j', 4095); + + printf("Inserting 1000 Records ... "); + for($i=0; $i<100; $i++) { + do_insert($db, $i * 10 + 1, $a1, $a10); + do_insert($db, $i * 10 + 2, $a2, $a9); + do_insert($db, $i * 10 + 3, $a3, $a8); + do_insert($db, $i * 10 + 4, $a4, $a7); + do_insert($db, $i * 10 + 5, $a5, $a6); + do_insert($db, $i * 10 + 6, $a6, $a5); + do_insert($db, $i * 10 + 7, $a7, $a4); + do_insert($db, $i * 10 + 8, $a8, $a3); + do_insert($db, $i * 10 + 9, $a9, $a2); + do_insert($db, $i * 10 + 10, $a10, $a1); + } + printf("Done\n"); + + /* Cleanup is done in pdo_oci_stream_2b.phpt */ + //$db->exec("drop table pdo_oci_stream_2"); + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + // Since each column only has one lob descriptor, the last row is + // shown twice because the lob descriptor for each column is reused in + // the stream + + $i = 0; + $j = 9; + $a_val = ord('a'); + foreach($db->query("select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id") as $row) { + $a = $row['d4_1']; + $a1 = $row['d4_2']; + + $str1 = stream_get_contents($a); + $str2 = stream_get_contents($a1); + + $str1len = strlen($str1); + $str2len = strlen($str2); + + $b = ord($str1[0]); + $b1 = ord($str2[0]); + + if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) && + ($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) { + printf("There is a bug!\n"); + printf("Col1:\n"); + printf("a_val = %d\n", $a_val); + printf("b = %d\n", $b); + printf("i = %d\n", $i); + printf("str1len = %d\n", $str1len); + + printf("Col2:\n"); + printf("a_val = %d\n", $a_val); + printf("b1 = %d\n", $b1); + printf("j = %d\n", $j); + printf("str2len = %d\n", $str1len); + + } + $i++; + if ($i>9) + $i = 0; + $j--; + if ($j<0) + $j = 9; + } + echo "Fetch operation done!\n"; + + /* Cleanup */ + $db->exec("drop table pdo_oci_stream_2"); +}); +?> +--EXPECT-- +Inserting 1000 Records ... Done +Fetch operation done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt new file mode 100644 index 00000000000..4828ac5e8c4 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt @@ -0,0 +1,88 @@ +--TEST-- +swoole_pdo_oracle: Test to verify all implicitly created temporary LOB are cleaned up +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $clobquery1 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery2 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery3 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery4 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery5 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery6 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery7 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery8 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery9 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery10 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + + $stmt= $db->prepare($clobquery1); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery2); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery3); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery4); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery5); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery6); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery7); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery8); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery9); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery10); + $stmt->execute(); + $row = $stmt->fetch(); + + $query1 = "SELECT SYS_CONTEXT('USERENV', 'SID') SID FROM DUAL"; + + $stmt1 = $db->prepare($query1); + $stmt1->execute(); + + $row1 = $stmt1->fetch(); + $sid_value = $row1[0]; + + $query2 = "SELECT (CACHE_LOBS+NOCACHE_LOBS+ABSTRACT_LOBS) FROM V\$TEMPORARY_LOBS WHERE SID = :SID_VALUE"; + + $stmt2 = $db->prepare($query2); + $stmt2->bindParam(':SID_VALUE', $sid_value); + $stmt2->execute(); + + $row2 = $stmt2->fetch(); + /* 1 temporary LOB still exists in V$TEMPORARY_LOBS since the destructor of $stmt is not yet called by PHP */ + if ($row2[0] > 1) + { + echo "TEMP_LOB is not yet cleared!" . $row2[0] . "\n"; + } + else + { + echo "Success! All the temporary LOB in previously closed statements are properly cleaned.\n"; + } +}); +?> +--EXPECTF-- +TEMP_LOB is not yet cleared!10 diff --git a/tests/swoole_pdo_oracle/pdo_oracle.inc b/tests/swoole_pdo_oracle/pdo_oracle.inc new file mode 100644 index 00000000000..3ebe26d8e4e --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oracle.inc @@ -0,0 +1,20 @@ +getMessage()); + } + } + + public static function create(): PDO + { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + return $db; + } +} diff --git a/tests/swoole_pdo_oracle/pecl_bug_11345.phpt b/tests/swoole_pdo_oracle/pecl_bug_11345.phpt new file mode 100644 index 00000000000..4b4fe28eb87 --- /dev/null +++ b/tests/swoole_pdo_oracle/pecl_bug_11345.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_pdo_oracle: PECL PDO_OCI Bug #11345 (Test invalid character set name) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $dbh = new PDO('oci:dbname=xxx;charset=yyy', 'abc', 'def'); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(). "\n"; + } +}); +?> +--EXPECTF-- +Connection failed: SQLSTATE[HY000]: OCINlsCharSetNameToId: unknown character set name (%s) diff --git a/tests/swoole_pdo_oracle/pecl_bug_6364.phpt b/tests/swoole_pdo_oracle/pecl_bug_6364.phpt new file mode 100644 index 00000000000..1fb60a9d22f --- /dev/null +++ b/tests/swoole_pdo_oracle/pecl_bug_6364.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_pdo_oracle: PECL PDO_OCI Bug #6364 (segmentation fault on stored procedure call with OUT binds) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->exec ("create table bug_6364_t (c1 varchar2(10), c2 varchar2(10), c3 varchar2(10), c4 varchar2(10), c5 varchar2(10))"); + + $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;"); + + $stmt = $dbh->prepare("call bug_6364_sp('p1','p2','p3',?,?)"); + + $out_param1 = "a"; + $out_param2 = "a"; + + $stmt->bindParam(1, $out_param1,PDO::PARAM_STR, 1024); + $stmt->bindParam(2, $out_param2,PDO::PARAM_STR, 1024); + + $stmt->execute() or die ("Execution error: " . var_dump($dbh->errorInfo())); + + var_dump($out_param1); + var_dump($out_param2); + + foreach ($dbh->query("select * from bug_6364_t") as $row) { + var_dump($row); + } + + print "Done\n"; + + // Cleanup + $dbh->exec ("drop procedure bug_6364_sp"); + $dbh->exec ("drop table bug_6364_t"); +}); +?> +--EXPECT-- +string(4) "val4" +string(4) "val5" +array(10) { + ["c1"]=> + string(2) "p1" + [0]=> + string(2) "p1" + ["c2"]=> + string(2) "p2" + [1]=> + string(2) "p2" + ["c3"]=> + string(2) "p3" + [2]=> + string(2) "p3" + ["c4"]=> + NULL + [3]=> + NULL + ["c5"]=> + NULL + [4]=> + NULL +} +Done diff --git a/tests/swoole_pdo_oracle/transcation.phpt b/tests/swoole_pdo_oracle/transcation.phpt new file mode 100644 index 00000000000..39bf771ac45 --- /dev/null +++ b/tests/swoole_pdo_oracle/transcation.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI transcation1 +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec('create table transcation1 (id int)'); + go(function () use($db){ + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation1 values (?)"); + $stmt->execute([1]); + go(function () use($db){ + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation1 values (?)"); + $stmt->execute([2]); + $db->rollback(); + }); + sleep(2); + $db->commit(); + $stmt = $db->prepare("select id from transcation1 where id = ?"); + $stmt->execute([1]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1); + }); + sleep(4); +}); +?> +--EXPECTF-- +Fatal error: Uncaught PDOException: There is already an active transaction in %s:%d +Stack trace: +#0 %s(%d): PDO->beginTransaction() +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_oracle/transcation2.phpt b/tests/swoole_pdo_oracle/transcation2.phpt new file mode 100644 index 00000000000..6ce9749f37d --- /dev/null +++ b/tests/swoole_pdo_oracle/transcation2.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_pdo_oracle: PDO OCI transcation2 +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec('create table transcation2 (id int)'); + + go(function() { + $db = PdoOracleTest::create(); + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation2 values (?)"); + $stmt->execute([1]); + $db->commit(); + $stmt = $db->prepare("select id from transcation2 where id = ?"); + $stmt->execute([1]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1); + }); + + go(function(){ + $db = PdoOracleTest::create(); + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation2 values (?)"); + $stmt->execute([2]); + $db->commit(); + $stmt = $db->prepare("select id from transcation2 where id = ?"); + $stmt->execute([2]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 2); + }); + sleep(1); + $db->exec('drop table transcation2'); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/tests/swoole_pdo_pgsql/base.phpt b/tests/swoole_pdo_pgsql/base.phpt new file mode 100644 index 00000000000..068ba9a855c --- /dev/null +++ b/tests/swoole_pdo_pgsql/base.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT * FROM pg_catalog.pg_tables limit 1'); + $statement->execute(); + var_dump($statement->fetchAll(PDO::FETCH_COLUMN)); + }); + + Co\go(function () { + $pdo = pdo_pgsql_test_inc::create(); + $statement = $pdo->prepare('SELECT * FROM pg_catalog.pg_tables limit 1'); + $statement->execute(); + var_dump($statement->fetchAll(PDO::FETCH_COLUMN)); + }); +}); + +echo "Done\n"; +?> +--EXPECTF-- +array(1) { + [0]=> + string(%d) "%s" +} +array(1) { + [0]=> + string(%d) "%s" +} +Done diff --git a/tests/swoole_pdo_pgsql/blocking.phpt b/tests/swoole_pdo_pgsql/blocking.phpt new file mode 100644 index 00000000000..737f3d59767 --- /dev/null +++ b/tests/swoole_pdo_pgsql/blocking.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + $statement->execute(); + Assert::eq($sleep_count, 0); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/bug_5635.phpt b/tests/swoole_pdo_pgsql/bug_5635.phpt new file mode 100644 index 00000000000..447d4d5d05c --- /dev/null +++ b/tests/swoole_pdo_pgsql/bug_5635.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_pdo_pgsql: Github bug #5635 +--SKIPIF-- + +--FILE-- +exec('create table bug_5635 (id int, data varchar(1024));'); +$pdo->exec(<< SWOOLE_HOOK_PDO_PGSQL]); +run(function() { + $waitGroup = new WaitGroup(); + $channel = new Channel(1); + + Coroutine::create(function() use ($waitGroup, $channel) { + $start = time(); + $waitGroup->add(); + $pdo = pdo_pgsql_test_inc::create(); + $stmt = $pdo->query("select * from bug_5635;"); + $data = $stmt->fetchAll(); + Assert::true(count($data) == 5000000); + $channel->push($data ?? [], 10); + $waitGroup->done(); + echo 'DONE' . PHP_EOL; + }); + + Coroutine::create(function() use ($waitGroup, $channel) { + $waitGroup->add(); + $result = $channel->pop(1.5); + if (!$result) { + echo 'channel pop timeout' . PHP_EOL; + } + $waitGroup->done(); + }); + + var_dump(1); + Coroutine::sleep(1); + var_dump(2); + $waitGroup->wait(); +}); +?> +--CLEAN-- +exec('drop table bug_5635;'); +?> +--EXPECTF-- +int(1) +int(2) +channel pop timeout +DONE diff --git a/tests/swoole_pdo_pgsql/pdo_pgsql.inc b/tests/swoole_pdo_pgsql/pdo_pgsql.inc new file mode 100644 index 00000000000..2e2c914a738 --- /dev/null +++ b/tests/swoole_pdo_pgsql/pdo_pgsql.inc @@ -0,0 +1,46 @@ +exec('CREATE TABLE ' . self::TABLE . ' ( + id SERIAL primary key NOT NULL, + name character varying(32), + age integer)'); + } + + $pdo->exec('TRUNCATE ' . self::TABLE); + } + + public static function getTable() + { + return self::TABLE; + } + + public static function tableExists($pdo, $table) + { + try { + $result = $pdo->query("SELECT 1 FROM {$table} LIMIT 1"); + } catch (Exception $e) { + return false; + } + + return $result !== false; + } + + public static function create(): PDO + { + $host = PGSQL_HOST; + $port = PGSQL_PORT; + $user = PGSQL_USER; + $password = PGSQL_PASSWORD; + $dbname = PGSQL_DBNAME; + return new PDO("pgsql:host={$host};port={$port};dbname={$dbname}", $user, $password); + } +} diff --git a/tests/swoole_pdo_pgsql/query.phpt b/tests/swoole_pdo_pgsql/query.phpt new file mode 100644 index 00000000000..99f35ce1a85 --- /dev/null +++ b/tests/swoole_pdo_pgsql/query.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)'); + + $list = []; + for ($i = 0; $i < N; $i++) { + $name = base64_encode(random_bytes(8)); + $age = random_int(18, 35); + $stmt->bindValue(1, $name); + $stmt->bindValue(2, $age); + $stmt->execute(); + + $list[] = [ + 'id' => $pdo->lastInsertId(), + 'name' => $name, + 'age' => $age, + ]; + } + + foreach ($list as $rs) { + Co\go(function () use ($rs) { + $pdo = pdo_pgsql_test_inc::create(); + $statement = $pdo->query('select * from ' . pdo_pgsql_test_inc::getTable() . ' where id = ' . $rs['id'] . ' limit 1'); + Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs); + }); + } +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/race.phpt b/tests/swoole_pdo_pgsql/race.phpt new file mode 100644 index 00000000000..172692a5ff8 --- /dev/null +++ b/tests/swoole_pdo_pgsql/race.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_pgsql: race +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + try { + $statement->execute(); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); + } catch (\PDOException $e) { + $msg[] = $e->getMessage(); + } + }); + } + Assert::count($msg, 1); + Assert::contains($msg[0], 'SQLSTATE[HY000]: General error'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/sleep.phpt b/tests/swoole_pdo_pgsql/sleep.phpt new file mode 100644 index 00000000000..ff931a70118 --- /dev/null +++ b/tests/swoole_pdo_pgsql/sleep.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + $statement->execute(); + Assert::eq($sleep_count, N); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/transaction.phpt b/tests/swoole_pdo_pgsql/transaction.phpt new file mode 100644 index 00000000000..e7a594e3df4 --- /dev/null +++ b/tests/swoole_pdo_pgsql/transaction.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)'); + $stmt->bindValue(1, base64_encode(random_bytes(8))); + $stmt->bindValue(2, random_int(18, 35)); + $stmt->execute(); + + var_dump('insert'); + + Co::join([Co\go(static function (): void { + $pdo = pdo_pgsql_test_inc::create(); + try { + $pdo->beginTransaction(); + + $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable()); + throw new Exception('interrupt!!!'); + $pdo->commit(); + } catch (\Exception $e) { + $pdo->rollBack(); + var_dump('rollback'); + } + })]); + + var_dump('wait1'); + var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable())); + + Co::join([Co\go(static function (): void { + $pdo = pdo_pgsql_test_inc::create(); + try { + $pdo->beginTransaction(); + + $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable()); + $pdo->commit(); + } catch (\Exception $e) { + $pdo->rollBack(); + var_dump($e->getMessage()); + } + })]); + + var_dump('wait2'); + var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable())); +}); + +echo "Done\n"; +?> +--EXPECTF-- +string(6) "insert" +string(8) "rollback" +string(5) "wait1" +bool(true) +string(5) "wait2" +bool(false) +Done diff --git a/tests/swoole_pdo_sqlite/bug33841.phpt b/tests/swoole_pdo_sqlite/bug33841.phpt new file mode 100644 index 00000000000..3c9cf6cbecc --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug33841.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLite Bug #33841 (rowCount() does not work on prepared statements) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + + $db->exec('CREATE TABLE test (text)'); + + $stmt = $db->prepare("INSERT INTO test VALUES ( :text )"); + $stmt->bindParam(':text', $name); + $name = 'test1'; + var_dump($stmt->execute(), $stmt->rowCount()); + + $stmt = $db->prepare("UPDATE test SET text = :text "); + $stmt->bindParam(':text', $name); + $name = 'test2'; + var_dump($stmt->execute(), $stmt->rowCount()); +}); +?> +--EXPECT-- +bool(true) +int(1) +bool(true) +int(1) diff --git a/tests/swoole_pdo_sqlite/bug35336.phpt b/tests/swoole_pdo_sqlite/bug35336.phpt new file mode 100644 index 00000000000..dc379bbcb1c --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug35336.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_sqlite:FETCH_CLASS + __set()) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $a = new PDO("sqlite::memory:");// pool ("sqlite::memory:"); + $a->query ("CREATE TABLE test (a integer primary key, b text)"); + $b = $a->prepare("insert into test (b) values (?)"); + $b->execute(array (5)); + $rez = $a->query ("SELECT * FROM test")->fetchAll(PDO::FETCH_CLASS, 'EEE'); + + echo "Done\n"; +}); +?> +--EXPECT-- +hello world +hello world +Done diff --git a/tests/swoole_pdo_sqlite/bug38334.phpt b/tests/swoole_pdo_sqlite/bug38334.phpt new file mode 100644 index 00000000000..0678500209e --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug38334.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_pdo_sqlite: Proper data-type support for PDO_SQLITE +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE test (i INTEGER , f DOUBLE, s VARCHAR(255))'); + $db->exec('INSERT INTO test VALUES (42, 46.7, "test")'); + var_dump($db->query('SELECT * FROM test')->fetch(PDO::FETCH_ASSOC)); + + // Check handling of integers larger than 32-bit. + $db->exec('INSERT INTO test VALUES (10000000000, 0.0, "")'); + $i = $db->query('SELECT i FROM test WHERE f = 0.0')->fetchColumn(0); + if (PHP_INT_SIZE >= 8) { + var_dump($i === 10000000000); + } else { + var_dump($i === '10000000000'); + } + + // Check storing of strings into integer/float columns. + $db->exec('INSERT INTO test VALUES ("test", "test", "x")'); + var_dump($db->query('SELECT * FROM test WHERE s = "x"')->fetch(PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(3) { + ["i"]=> + int(42) + ["f"]=> + float(46.7) + ["s"]=> + string(4) "test" +} +bool(true) +array(3) { + ["i"]=> + string(4) "test" + ["f"]=> + string(4) "test" + ["s"]=> + string(1) "x" +} diff --git a/tests/swoole_pdo_sqlite/bug43831.phpt b/tests/swoole_pdo_sqlite/bug43831.phpt new file mode 100644 index 00000000000..7157e332d7f --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug43831.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_pdo_sqlite: Bug #43831 ($this gets mangled when extending PDO with persistent connection) +--SKIPIF-- + + +--FILE-- + true)); + } +} + +class Baz extends PDO { + function __construct($dsn) { + parent::__construct($dsn, null, null, array(PDO::ATTR_PERSISTENT => true)); + } +} + +class Bar extends Baz { + function quux() { + echo get_class($this), "\n"; + $foo = new Foo("sqlite::memory:"); + echo get_class($this), "\n"; + } +} + +class MyPDO extends PDO {} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $bar = new Bar("sqlite::memory:"); + $bar->quux(); + + $bar = new PDO("sqlite::memory:", null, null, array(PDO::ATTR_PERSISTENT => true)); + $baz = new MyPDO("sqlite::memory:", null, null, array(PDO::ATTR_PERSISTENT => true)); + + var_dump($bar); + unset($bar); + var_dump($baz); + var_dump($bar); +}); +?> +--EXPECTF-- +Bar +Bar +object(PDO)#%d (0) { +} +object(MyPDO)#%d (0) { +} + +Warning: Undefined variable $bar in %s on line %d +NULL diff --git a/tests/swoole_pdo_sqlite/bug44327_2_1.phpt b/tests/swoole_pdo_sqlite/bug44327_2_1.phpt new file mode 100644 index 00000000000..751296b0808 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_2_1.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(); + var_dump($y, @$y->queryString); + + print "--------------------------------------------\n"; + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +array(2) { + ["queryString"]=> + string(1) "1" + [0]=> + string(1) "1" +} +NULL +-------------------------------------------- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +object(PDORow)#%d (1) { + ["queryString"]=> + string(1) "1" +} +string(1) "1" diff --git a/tests/swoole_pdo_sqlite/bug44327_2_2.phpt b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt new file mode 100644 index 00000000000..ae159df11cc --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(); + var_dump($y, @$y->queryString); + + print "--------------------------------------------\n"; + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +array(2) { + ["queryString"]=> + int(1) + [0]=> + int(1) +} +NULL +-------------------------------------------- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +object(PDORow)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" diff --git a/tests/swoole_pdo_sqlite/bug44327_3_1.phpt b/tests/swoole_pdo_sqlite/bug44327_3_1.phpt new file mode 100644 index 00000000000..065cdab4378 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_3_1.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryStringxx'); + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx); + + print "---\n"; + + var_dump($y[5], $y->{3}); +}); +?> +--EXPECTF-- +object(PDORow)#%d (2) { + ["queryString"]=> + string(25) "select 1 as queryStringxx" + ["queryStringxx"]=> + string(1) "1" +} +string(25) "select 1 as queryStringxx" +NULL +string(1) "1" +--- +NULL +NULL diff --git a/tests/swoole_pdo_sqlite/bug44327_3_2.phpt b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt new file mode 100644 index 00000000000..3f1a16ab464 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryStringxx'); + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx); + + print "---\n"; + + var_dump($y[5], $y->{3}); +}); +?> +--EXPECTF-- +object(PDORow)#%d (2) { + ["queryString"]=> + string(25) "select 1 as queryStringxx" + ["queryStringxx"]=> + int(1) +} +string(25) "select 1 as queryStringxx" +NULL +int(1) +--- +NULL +NULL diff --git a/tests/swoole_pdo_sqlite/bug46139.phpt b/tests/swoole_pdo_sqlite/bug46139.phpt new file mode 100644 index 00000000000..2926e93a402 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug46139.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_pdo_sqlite: Bug #46139 (PDOStatement->setFetchMode() forgets FETCH_PROPS_LATE) +--SKIPIF-- + + +--FILE-- +test); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetch(); + printf("'%s'\n", $r1->test); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetchAll(); + printf("'%s'\n", $r1[0]->test); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetch(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE); + printf("'%s'\n", $r1->test); +}); +?> +--EXPECT-- +NULL +'foo' +NULL +'foo' +NULL +'foo' diff --git a/tests/swoole_pdo_sqlite/bug46542.phpt b/tests/swoole_pdo_sqlite/bug46542.phpt new file mode 100644 index 00000000000..07eeec10d7e --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug46542.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_pdo_sqlite: Bug #46542 Extending PDO class with a __call() function +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $a = new A('sqlite:' . __DIR__ . '/dummy.db'); + + $a->truc(); + $a->TRUC(); +}); +?> +--CLEAN-- + +--EXPECT-- +A::truc +A::TRUC diff --git a/tests/swoole_pdo_sqlite/bug48773.phpt b/tests/swoole_pdo_sqlite/bug48773.phpt new file mode 100644 index 00000000000..6d5b53a7977 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug48773.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_pdo_sqlite:ATTR_STATEMENT_CLASS with ctor_args) +--SKIPIF-- + + +--FILE-- +setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this))); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new foo('sqlite::memory:', '', ''); + $stmt = $db->query('SELECT 1'); + var_dump($stmt); +}); +?> +--EXPECTF-- +object(bar)#%d (1) { + ["queryString"]=> + string(8) "SELECT 1" +} diff --git a/tests/swoole_pdo_sqlite/bug50728.phpt b/tests/swoole_pdo_sqlite/bug50728.phpt new file mode 100644 index 00000000000..d5580002095 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug50728.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_pdo_sqlite: Bug #50728 (All PDOExceptions hardcode 'code' property to 0) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + try { + $a = new PDO("sqlite:/this/path/should/not/exist.db"); + } catch (PDOException $e) { + var_dump($e->getCode()); + } +}); +?> +--EXPECT-- +int(14) diff --git a/tests/swoole_pdo_sqlite/bug52487.phpt b/tests/swoole_pdo_sqlite/bug52487.phpt new file mode 100644 index 00000000000..b34b2bb9355 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug52487.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_pdo_sqlite:FETCH_INTO leaks memory) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + $stmt = $db->prepare("select 1 as attr"); + for ($i = 0; $i < 10; $i++) { + $stmt->setFetchMode(PDO::FETCH_INTO, new stdClass); + } + + print "ok\n"; +}); +?> +--EXPECT-- +ok diff --git a/tests/swoole_pdo_sqlite/bug60104.phpt b/tests/swoole_pdo_sqlite/bug60104.phpt new file mode 100644 index 00000000000..60aec82461b --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug60104.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_pdo_sqlite: Bug #60104 (Segmentation Fault in pdo_sqlite when using sqliteCreateFunction()) +--SKIPIF-- + + +--FILE-- +sqliteCreateFunction( "md5", "md5", 1 ); + unset( $handler ); +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + setUp(); + setUp(); + echo "done"; +}); +?> +--EXPECT-- +done diff --git a/tests/swoole_pdo_sqlite/bug66033.phpt b/tests/swoole_pdo_sqlite/bug66033.phpt new file mode 100644 index 00000000000..496005a3785 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug66033.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_pdo_sqlite: Bug #66033 (Segmentation Fault when constructor of PDO statement throws an exception) +--SKIPIF-- + + +--FILE-- +dbh = $dbh; + throw new Exception("Blah"); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null); + $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DBStatement', + array($pdo))); + $pdo->exec("CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY, + title TEXT, + message TEXT, + time INTEGER)"); + + try { + $pdoStatement = $pdo->query("select * from messages"); + } catch (Exception $e) { + var_dump($e->getMessage()); + } +}); +?> +--EXPECT-- +string(4) "Blah" diff --git a/tests/swoole_pdo_sqlite/bug70221.phpt b/tests/swoole_pdo_sqlite/bug70221.phpt new file mode 100644 index 00000000000..05b1b61e7ac --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70221.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_pdo_sqlite: Bug #70221 (persistent sqlite connection + custom function segfaults) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $dbfile = __DIR__ . '/test.sqlite'; + $db = new PDO('sqlite:'.$dbfile, null, null, array(PDO::ATTR_PERSISTENT => true)); + function _test() { return 42; } + $db->sqliteCreateFunction('test', '_test', 0); + print("Everything is fine, no exceptions here\n"); + unset($db); +}); +?> +--CLEAN-- + +--EXPECT-- +Everything is fine, no exceptions here diff --git a/tests/swoole_pdo_sqlite/bug70862.phpt b/tests/swoole_pdo_sqlite/bug70862.phpt new file mode 100644 index 00000000000..a783456187b --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70862.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- += 80200) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.1 or lower'); +} +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->exec('CREATE TABLE test(field BLOB)'); + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + class HelloWrapper { + public function stream_open() { return true; } + public function stream_eof() { return true; } + public function stream_read() { return NULL; } + public function stream_stat() { return array(); } + } + stream_wrapper_register("hello", "HelloWrapper"); + + $f = fopen("hello://there", "r"); + + $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)'); + $stmt->bindParam(":para", $f, PDO::PARAM_LOB); + $stmt->execute(); + + var_dump($f); +}); +?> ++++DONE+++ +--EXPECT-- +string(0) "" ++++DONE+++ diff --git a/tests/swoole_pdo_sqlite/bug70862_1.phpt b/tests/swoole_pdo_sqlite/bug70862_1.phpt new file mode 100644 index 00000000000..bbf85654017 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70862_1.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->exec('CREATE TABLE test(field BLOB)'); + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + class HelloWrapper { + public function stream_open() { return true; } + public function stream_eof() { return true; } + public function stream_read() { return NULL; } + public function stream_stat() { return array(); } + } + stream_wrapper_register("hello", "HelloWrapper"); + + $f = fopen("hello://there", "r"); + + $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)'); + $stmt->bindParam(":para", $f, PDO::PARAM_LOB); + $stmt->execute(); + + var_dump($f); +}); +?> ++++DONE+++ +--EXPECTF-- + +Deprecated: Creation of dynamic property HelloWrapper::$context is deprecated in %s on line %d +string(0) "" ++++DONE+++ diff --git a/tests/swoole_pdo_sqlite/bug78192_1.phpt b/tests/swoole_pdo_sqlite/bug78192_1.phpt new file mode 100644 index 00000000000..a415a55e677 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug78192_1.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLite Bug #78192 SegFault when reuse statement after schema change +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $connection = new \PDO('sqlite::memory:'); + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)'); + + $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)'); + $stmt->execute([ + 'id' => 10, + 'name' => 'test', + ]); + + $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); + + $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["id"]=> + string(2) "10" + ["name"]=> + string(4) "test" + } +} +array(1) { + [0]=> + array(3) { + ["id"]=> + string(2) "10" + ["name"]=> + string(4) "test" + ["new_col"]=> + NULL + } +} diff --git a/tests/swoole_pdo_sqlite/bug78192_2.phpt b/tests/swoole_pdo_sqlite/bug78192_2.phpt new file mode 100644 index 00000000000..67c52bf8367 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug78192_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLite Bug #78192 SegFault when reuse statement after schema change +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $connection = new \PDO('sqlite::memory:'); + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)'); + + $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)'); + $stmt->execute([ + 'id' => 10, + 'name' => 'test', + ]); + + $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); + + $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["id"]=> + int(10) + ["name"]=> + string(4) "test" + } +} +array(1) { + [0]=> + array(3) { + ["id"]=> + int(10) + ["name"]=> + string(4) "test" + ["new_col"]=> + NULL + } +} diff --git a/tests/swoole_pdo_sqlite/bug79664_1.phpt b/tests/swoole_pdo_sqlite/bug79664_1.phpt new file mode 100644 index 00000000000..311deed1084 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug79664_1.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_pdo_sqlite:getColumnMeta fails on empty result set) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ]); + $stmt = $pdo->query('select 1 where 0'); + if ($stmt->columnCount()) { + var_dump($stmt->getColumnMeta(0)); + } +}); +?> +--EXPECT-- +array(6) { + ["native_type"]=> + string(4) "null" + ["flags"]=> + array(0) { + } + ["name"]=> + string(1) "1" + ["len"]=> + int(-1) + ["precision"]=> + int(0) + ["pdo_type"]=> + int(2) +} diff --git a/tests/swoole_pdo_sqlite/bug79664_2.phpt b/tests/swoole_pdo_sqlite/bug79664_2.phpt new file mode 100644 index 00000000000..daffb554edf --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug79664_2.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_pdo_sqlite:getColumnMeta fails on empty result set) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ]); + $stmt = $pdo->query('select 1 where 0'); + if ($stmt->columnCount()) { + var_dump($stmt->getColumnMeta(0)); + } +}); +?> +--EXPECT-- +array(6) { + ["native_type"]=> + string(4) "null" + ["pdo_type"]=> + int(0) + ["flags"]=> + array(0) { + } + ["name"]=> + string(1) "1" + ["len"]=> + int(-1) + ["precision"]=> + int(0) +} diff --git a/tests/swoole_pdo_sqlite/bug81740.phpt b/tests/swoole_pdo_sqlite/bug81740.phpt new file mode 100644 index 00000000000..14d3d4f15f7 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug81740.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_pdo_sqlite:quote() may return unquoted string) +--SKIPIF-- + + +--INI-- +memory_limit=-1 +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite::memory:"); + $string = str_repeat("a", 0x80000000); + var_dump($pdo->quote($string)); +}); +?> +--EXPECT-- +bool(false) diff --git a/tests/swoole_pdo_sqlite/bug_42589.phpt b/tests/swoole_pdo_sqlite/bug_42589.phpt new file mode 100644 index 00000000000..a561a3cc4ef --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_42589.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLite Feature Request #42589 (getColumnMeta() should also return table name) +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO("sqlite::memory:"); + + $db->exec('CREATE TABLE test (field1 VARCHAR(10))'); + $db->exec('INSERT INTO test VALUES("test")'); + + $result = $db->query('SELECT * FROM test t1 LEFT JOIN test t2 ON t1.field1 = t2.field1'); + $meta1 = $result->getColumnMeta(0); + $meta2 = $result->getColumnMeta(1); + + var_dump(!empty($meta1['table']) && $meta1['table'] == 'test'); + var_dump(!empty($meta2['table']) && $meta2['table'] == 'test'); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt new file mode 100644 index 00000000000..8f5b5388f08 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_sqlite: SQLite variant +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite:".__DIR__."/foo.db"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL)); + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1)); + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense')); + + @unlink(__DIR__."/foo.db"); +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt new file mode 100644 index 00000000000..e474199e568 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_pdo_sqlite: SQLite variant +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite:".__DIR__."/foo.db"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + try { + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1)); + try { + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + @unlink(__DIR__."/foo.db"); +}); +?> +--EXPECT-- +Attribute value must be of type int for selected attribute, null given +bool(true) +Attribute value must be of type int for selected attribute, string given diff --git a/tests/swoole_pdo_sqlite/bug_47769.phpt b/tests/swoole_pdo_sqlite/bug_47769.phpt new file mode 100644 index 00000000000..7fe5a5a9d07 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_47769.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_pdo_sqlite: Bug #47769 (Strange extends PDO) +--SKIPIF-- + + +--FILE-- +isProtected(); + $this->isPrivate(); + print $str ."\n"; + + return $str; + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $test = new test('sqlite::memory:'); + $test->quote('foo'); + $test->isProtected(); +}); +?> +--EXPECTF-- +this is a protected method. +this is a private method. +foo + +Fatal error: Uncaught Error: Call to protected method test::isProtected() from global scope in %s:%d +Stack trace: +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_sqlite/bug_63916-2.phpt b/tests/swoole_pdo_sqlite/bug_63916-2.phpt new file mode 100644 index 00000000000..365c0a3c198 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916-2.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- + + 4) die('skip 32-bit only'); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = PHP_INT_MAX; // 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(2147483647) +int(2147483647) diff --git a/tests/swoole_pdo_sqlite/bug_63916_1.phpt b/tests/swoole_pdo_sqlite/bug_63916_1.phpt new file mode 100644 index 00000000000..0a917fdfaf5 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916_1.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +if (PHP_INT_SIZE < 8) die('skip'); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = 100004313234244; // exceeds 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(100004313234244) +string(15) "100004313234244" diff --git a/tests/swoole_pdo_sqlite/bug_63916_2.phpt b/tests/swoole_pdo_sqlite/bug_63916_2.phpt new file mode 100644 index 00000000000..4cd75f352be --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916_2.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = 100004313234244; // exceeds 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(100004313234244) +int(100004313234244) diff --git a/tests/swoole_pdo_sqlite/bug_64705.phpt b/tests/swoole_pdo_sqlite/bug_64705.phpt new file mode 100644 index 00000000000..4685087c948 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_64705.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_pdo_sqlite:__construct() fails +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $dsn = 'sqlite:./bug64705NonExistingDir/bug64705NonExistingDb'; + try { + $pdo = new \PDO($dsn, null, null); + } catch (\PDOException $e) { + var_dump(!empty($e->errorInfo) && is_array($e->errorInfo)); + } +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_pdo_sqlite/coroutine.phpt b/tests/swoole_pdo_sqlite/coroutine.phpt new file mode 100644 index 00000000000..0912bb55e92 --- /dev/null +++ b/tests/swoole_pdo_sqlite/coroutine.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLITE coroutine +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('create table test (id int)'); + for($i = 0; $i <= 20; $i++) { + go(function() use ($i, $db) { + $stmt = $db->prepare('insert into test values(?)'); + $stmt->execute([$i]); + $stmt = $db->prepare('select id from test where id = ?'); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/coroutine2.phpt b/tests/swoole_pdo_sqlite/coroutine2.phpt new file mode 100644 index 00000000000..1b884d05c29 --- /dev/null +++ b/tests/swoole_pdo_sqlite/coroutine2.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLITE coroutine +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +$db = new PDO('sqlite:test.db'); +$db->exec('create table if not exists test (id int)'); +$db->exec('delete from test'); + +run(function() { + for($i = 0; $i <= 20; $i++) { + go(function() use ($i) { + $db = new PDO('sqlite:test.db'); + $stmt = $db->prepare('insert into test values(?)'); + $stmt->execute([$i]); + $stmt = $db->prepare('select id from test where id = ?'); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt b/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt new file mode 100644 index 00000000000..fef462db10a --- /dev/null +++ b/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_pdo_sqlite:debugDumpParams() with bound params +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x= $db->prepare('select :a, :b, ?'); + $x->bindValue(':a', 1, PDO::PARAM_INT); + $x->bindValue(':b', 'foo'); + $x->bindValue(3, 1313); + var_dump($x->debugDumpParams()); +}); +?> +--EXPECT-- +SQL: [16] select :a, :b, ? +Params: 3 +Key: Name: [2] :a +paramno=-1 +name=[2] ":a" +is_param=1 +param_type=1 +Key: Name: [2] :b +paramno=-1 +name=[2] ":b" +is_param=1 +param_type=2 +Key: Position #2: +paramno=2 +name=[0] "" +is_param=1 +param_type=2 +NULL diff --git a/tests/swoole_pdo_sqlite/gc.phpt b/tests/swoole_pdo_sqlite/gc.phpt new file mode 100644 index 00000000000..8b3c9c4cf54 --- /dev/null +++ b/tests/swoole_pdo_sqlite/gc.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_pdo_sqlite: GC support for PDO Sqlite driver data +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $obj = new Obj; + $obj->a = new PDO('sqlite::memory:'); + $obj->a->sqliteCreateFunction('func1', function() use ($obj) {}, 1); + $obj->a->sqliteCreateAggregate('func2', function() use ($obj) {}, function() use($obj) {}); + $obj->a->sqliteCreateCollation('col', function() use ($obj) {}); +}); +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/tests/swoole_pdo_sqlite/gh9032.phpt b/tests/swoole_pdo_sqlite/gh9032.phpt new file mode 100644 index 00000000000..d4f569bcc1f --- /dev/null +++ b/tests/swoole_pdo_sqlite/gh9032.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_sqlite: SQLite3 authorizer crashes on NULL values +--SKIPIF-- + + +--INI-- +open_basedir=. +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + $db->exec('attach database \':memory:\' AS "db1"'); + var_dump($db->exec('create table db1.r (id int)')); + + try { + $st = $db->prepare('attach database :a AS "db2"'); + $st->execute([':a' => ':memory:']); + var_dump($db->exec('create table db2.r (id int)')); + } catch (PDOException $ex) { + echo $ex->getMessage(), PHP_EOL; + } +}); +?> +--EXPECT-- +int(0) +SQLSTATE[HY000]: General error: 23 not authorized diff --git a/tests/swoole_pdo_sqlite/open_basedir.phpt b/tests/swoole_pdo_sqlite/open_basedir.phpt new file mode 100644 index 00000000000..aa316d1534f --- /dev/null +++ b/tests/swoole_pdo_sqlite/open_basedir.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_pdo_sqlite: PDO SQLite open_basedir check +--SKIPIF-- + +--INI-- +open_basedir=. +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + chdir(__DIR__); + + try { + $db = new PDO('sqlite:../not_in_open_basedir.sqlite'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + try { + $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + try { + $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite?mode=ro'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +}); +?> +--EXPECT-- +open_basedir prohibits opening ../not_in_open_basedir.sqlite +open_basedir prohibits opening file:../not_in_open_basedir.sqlite +open_basedir prohibits opening file:../not_in_open_basedir.sqlite?mode=ro diff --git a/tests/swoole_pdo_sqlite/pdo_035.phpt b/tests/swoole_pdo_sqlite/pdo_035.phpt new file mode 100644 index 00000000000..c91f6fbec91 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_035.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_pdo_sqlite: PDORow + get_parent_class() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE test (id int)'); + $db->exec('INSERT INTO test VALUES (23)'); + + $stmt = $db->prepare('SELECT id FROM test'); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_LAZY); + + echo get_class($result), "\n"; + var_dump(get_parent_class($result)); + + try { + $result->foo = 1; + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + $result[0] = 1; + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + unset($result->foo); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + unset($result[0]); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } +}); +?> +--EXPECT-- +PDORow +bool(false) +Cannot write to PDORow property +Cannot write to PDORow offset +Cannot unset PDORow property +Cannot unset PDORow offset diff --git a/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt b/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt new file mode 100644 index 00000000000..6b8fbbb5327 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt @@ -0,0 +1,154 @@ +--TEST-- +swoole_pdo_sqlite:FETCH_FUNC +--SKIPIF-- += 80200) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.1 or lower'); +} +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)'); + $db->exec('INSERT INTO testing VALUES(1, "php")'); + $db->exec('INSERT INTO testing VALUES(2, "")'); + + $st = $db->query('SELECT * FROM testing'); + $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print "data: $x, $y\n"; }); + + $st = $db->query('SELECT name FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, '')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 1)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + class foo { + public function method($x) { + return "--- $x ---"; + } + } + class bar extends foo { + public function __construct($db) { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method'))); + } + + static public function test($x, $y) { + return $x .'---'. $y; + } + + private function test2($x, $y) { + return $x; + } + + public function test3($x, $y) { + return $x .'==='. $y; + } + } + + new bar($db); + + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test'))); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 1, php +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 2, +array(2) { + [0]=> + string(3) "PHP" + [1]=> + string(0) "" +} +function "nothing" not found or invalid function name +function "" not found or invalid function name +PDOStatement::fetchAll(): Argument #2 must be a callable, null given +no array or string given +cannot access "self" when no class scope is active +array(2) { + [0]=> + string(9) "--- 1 ---" + [1]=> + string(9) "--- 2 ---" +} +array(2) { + [0]=> + string(7) "1---php" + [1]=> + string(4) "2---" +} +non-static method bar::test2() cannot be called statically +non-static method bar::test3() cannot be called statically +class bar does not have a method "inexistent" diff --git a/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt new file mode 100644 index 00000000000..a01364b6e92 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt @@ -0,0 +1,156 @@ +--TEST-- +swoole_pdo_sqlite:FETCH_FUNC +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)'); + $db->exec('INSERT INTO testing VALUES(1, "php")'); + $db->exec('INSERT INTO testing VALUES(2, "")'); + + $st = $db->query('SELECT * FROM testing'); + $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print "data: $x, $y\n"; }); + + $st = $db->query('SELECT name FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, '')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 1)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + class foo { + public function method($x) { + return "--- $x ---"; + } + } + class bar extends foo { + public function __construct($db) { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method'))); + } + + static public function test($x, $y) { + return $x .'---'. $y; + } + + private function test2($x, $y) { + return $x; + } + + public function test3($x, $y) { + return $x .'==='. $y; + } + } + + new bar($db); + + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test'))); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 1, php +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 2, +array(2) { + [0]=> + string(3) "PHP" + [1]=> + string(0) "" +} +function "nothing" not found or invalid function name +function "" not found or invalid function name +PDOStatement::fetchAll(): Argument #2 must be a callable, null given +no array or string given +cannot access "self" when no class scope is active + +Deprecated: Callables of the form ["bar", "parent::method"] are deprecated in %s on line %d +array(2) { + [0]=> + string(9) "--- 1 ---" + [1]=> + string(9) "--- 2 ---" +} +array(2) { + [0]=> + string(7) "1---php" + [1]=> + string(4) "2---" +} +non-static method bar::test2() cannot be called statically +non-static method bar::test3() cannot be called statically +class bar does not have a method "inexistent" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite.inc b/tests/swoole_pdo_sqlite/pdo_sqlite.inc new file mode 100644 index 00000000000..84c983dd69a --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite.inc @@ -0,0 +1,18 @@ +getMessage()); + } + } + + public static function create(): PDO + { + return new PDO(SQLITE_DSN); + } +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt new file mode 100644 index 00000000000..cc0b145e6f8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateAggregate() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + $db->sqliteCreateAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(2) "12" + [0]=> + string(2) "12" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 00000000000..61e98d87f1e --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_pdo_sqlite: Testing invalid callback for sqliteCreateAggregate() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:'); + + try { + $pdo->sqliteCreateAggregate('foo', 'a', ''); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } + try { + $pdo->sqliteCreateAggregate('foo', 'strlen', ''); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } +}); +?> +--EXPECT-- +PDO::sqliteCreateAggregate(): Argument #2 ($step) must be a valid callback, function "a" not found or invalid function name +PDO::sqliteCreateAggregate(): Argument #3 ($finalize) must be a valid callback, function "" not found or invalid function name diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt new file mode 100644 index 00000000000..4ce270a708c --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "1")'); + $db->query('INSERT INTO foobar VALUES (NULL, "2")'); + $db->query('INSERT INTO foobar VALUES (NULL, "10")'); + + $db->sqliteCreateCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + + $result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); + foreach ($result as $row) { + echo $row['name'] . "\n"; + } + + $result = $db->query('SELECT name FROM foobar ORDER BY name'); + foreach ($result as $row) { + echo $row['name'] . "\n"; + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt new file mode 100644 index 00000000000..deb7145f297 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateFunction() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + + $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }); + + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt new file mode 100644 index 00000000000..9e08fae1d95 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateFunction() produces warning when +un-callable function passed +--CREDITS-- +Chris MacPherson chris@kombine.co.uk +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO( 'sqlite::memory:'); + + try { + $db->sqliteCreateFunction('bar-alias', 'bar'); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } +}); +?> +--EXPECT-- +PDO::sqliteCreateFunction(): Argument #2 ($callback) must be a valid callback, function "bar" not found or invalid function name diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt new file mode 100644 index 00000000000..e88ab026611 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_pdo_sqlite: Testing sqliteCreateFunction() with flags +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + + $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }, 1, PDO::SQLITE_DETERMINISTIC); + + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt new file mode 100644 index 00000000000..844e883f376 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_pdo_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + echo "Creating new PDO" . PHP_EOL; + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )"); + + echo "Inserting first time which should succeed" . PHP_EOL; + $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("First Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + echo "Inserting second time which should fail" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + + echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL; + $db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )"); + + echo "Inserting first time which should succeed" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("First (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + echo "Inserting second time which should fail" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("Second (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; +}); +?> +--EXPECT-- +Creating new PDO +Inserting first time which should succeed +First Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: () +Inserting second time which should fail +Second Error Info: SQLSTATE Error Code: (23000), Driver Specific Error Code: (19) +Creating new PDO with Extended Result Codes turned on +Inserting first time which should succeed +First (Extended) Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: () +Inserting second time which should fail +Second (Extended) Error Info: SQLSTATE Error Code: (HY000), Driver Specific Error Code: (1555) diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt new file mode 100644 index 00000000000..6f44f1e5b8f --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_pdo_sqlite: Testing filename uri +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + // create with default read-write|create mode + $filename = "file:" . __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_filename_uri.db"; + + $db = new PDO('sqlite:' . $filename); + + var_dump($db->exec('CREATE TABLE test1 (id INT);')); + + // create with readonly mode + $filename = "file:" . __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_filename_uri.db?mode=ro"; + + $db = new PDO('sqlite:' . $filename); + + var_dump($db->exec('CREATE TABLE test2 (id INT);')); +}); +?> +--CLEAN-- + +--EXPECTF-- +int(0) + +Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s +Stack trace: +%s +%A + thrown in %s diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt new file mode 100644 index 00000000000..27355a325f1 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_pdo_sqlite: Testing getAttribute() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:'); + var_dump($pdo->getAttribute(PDO::ATTR_SERVER_VERSION)); + var_dump($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION)); +}); +?> +--EXPECTF-- +string(%d) "%s" +string(%d) "%s" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt new file mode 100644 index 00000000000..bb996b3f5e8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_pdo_sqlite: Testing lastInsertId() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->query('CREATE TABLE IF NOT EXISTS foo (id INT AUTO INCREMENT, name TEXT)'); + $db->query('INSERT INTO foo VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foo VALUES (NULL, "PHP6")'); + var_dump($db->query('SELECT * FROM foo')); + var_dump($db->errorInfo()); + var_dump($db->lastInsertId()); + + $db->query('DROP TABLE foo'); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(17) "SELECT * FROM foo" +} +array(3) { + [0]=> + string(5) "00000" + [1]=> + NULL + [2]=> + NULL +} +string(1) "2" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt new file mode 100644 index 00000000000..d32e380fe3f --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_sqlite: Testing open flags +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_open_flags.db"; + + // Default open flag is read-write|create + $db = new PDO('sqlite:' . $filename, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + var_dump($db->exec('CREATE TABLE test1 (id INT);')); + + $db = new PDO('sqlite:' . $filename, null, null, [PDO::SQLITE_ATTR_OPEN_FLAGS => PDO::SQLITE_OPEN_READONLY, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + var_dump($db->exec('CREATE TABLE test2 (id INT);')); + + $db->exec('drop table test1'); + $db->exec('drop table test2'); +}); +?> +--CLEAN-- + +--EXPECTF-- +int(0) + +Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s +Stack trace: +%s +%A + thrown in %s diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt new file mode 100644 index 00000000000..5e04350731a --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_pdo_sqlite:getAttribute() +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $st = $db->prepare('SELECT 1;'); + + var_dump($st->getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT)); + + $st = $db->prepare('CREATE TABLE test (a TEXT);'); + + var_dump($st->getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT)); +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt new file mode 100644 index 00000000000..a103b2e0e13 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_pdo_sqlite: __toString() exception during PDO Sqlite parameter binding +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE t(id int, v varchar(255))'); + + $stmt = $db->prepare('INSERT INTO t VALUES(:i, :v)'); + $param1 = 1234; + $stmt->bindValue('i', $param1); + $param2 = "foo"; + $stmt->bindParam('v', $param2); + + $param2 = new throws; + + try { + $stmt->execute(); + } catch (Exception $e) { + echo "Exception thrown ...\n"; + } + + try { + $stmt->execute(); + } catch (Exception $e) { + echo "Exception thrown ...\n"; + } + + $query = $db->query("SELECT * FROM t"); + while ($row = $query->fetch(PDO::FETCH_ASSOC)) { + print_r($row); + } +}); +?> +--EXPECT-- +Exception thrown ... +Exception thrown ... diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt new file mode 100644 index 00000000000..7866dbfb478 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_pdo_sqlite: Testing transaction +--SKIPIF-- + + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->beginTransaction(); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + $db->commit(); + + $db->beginTransaction(); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + $db->rollback(); + + $r = $db->query('SELECT COUNT(*) FROM foobar'); + var_dump($r->rowCount()); + + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECTF-- +int(0) + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 6 database table is locked in %s on line %d diff --git a/tests/swoole_process/alarm.phpt b/tests/swoole_process/alarm.phpt index 4e3ed6d0de8..8f33b1e61d9 100644 --- a/tests/swoole_process/alarm.phpt +++ b/tests/swoole_process/alarm.phpt @@ -16,7 +16,7 @@ Process::signal(SIGALRM, function () { Process::alarm(-1); Process::signal(SIGALRM, null); Swoole\Event::del(STDIN); - swoole_event_exit(); + Swoole\Event::exit(); } }); diff --git a/tests/swoole_process/close.phpt b/tests/swoole_process/close.phpt new file mode 100644 index 00000000000..afb223030f9 --- /dev/null +++ b/tests/swoole_process/close.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_process: close +--SKIPIF-- + +--FILE-- +close(); +//}); +//$proc->start(); +// +//$proc = new Swoole\Process(swoole_function(Swoole\Process $proc) { +// usleep(200000); +// // Assert::true(false, 'never here'); +//}); +//$proc->start(); +//$proc->close(); +// +// +//\Swoole\Process::wait(true); +//\Swoole\Process::wait(true); +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/coro/ipc.phpt b/tests/swoole_process/coro/ipc.phpt index 8315e4df06f..ad310e09195 100644 --- a/tests/swoole_process/coro/ipc.phpt +++ b/tests/swoole_process/coro/ipc.phpt @@ -6,7 +6,7 @@ swoole_process/coro: ipc with coroutine exportSocket(); echo $socket->recv(); $socket->send("hello proc2\n"); @@ -15,7 +15,7 @@ $proc1 = new \swoole_process(function (swoole_process $proc) { Assert::assert($proc1->start()); -$proc2 = new \swoole_process(function (swoole_process $proc) use ($proc1) { +$proc2 = new Swoole\Process(function (Swoole\Process $proc) use ($proc1) { Co::sleep(0.01); $socket = $proc1->exportSocket(); $socket->send("hello proc1\n"); @@ -25,8 +25,8 @@ $proc2 = new \swoole_process(function (swoole_process $proc) use ($proc1) { Assert::assert($proc2->start()); -swoole_process::wait(true); -swoole_process::wait(true); +Swoole\Process::wait(true); +Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/coro/set_protocol.phpt b/tests/swoole_process/coro/set_protocol.phpt index 08979847cf1..77562285d20 100644 --- a/tests/swoole_process/coro/set_protocol.phpt +++ b/tests/swoole_process/coro/set_protocol.phpt @@ -38,7 +38,7 @@ while ($n--) { $proc1->write($pkg); } $proc1->write(pack('n', 0)); -swoole_process::wait(true); +Swoole\Process::wait(true); ?> --EXPECT-- END diff --git a/tests/swoole_process/coro/start.phpt b/tests/swoole_process/coro/start.phpt index 47fcf0f81b6..fc2095a00a2 100644 --- a/tests/swoole_process/coro/start.phpt +++ b/tests/swoole_process/coro/start.phpt @@ -6,7 +6,7 @@ swoole_process/coro: start with coroutine start(); Assert::assert($r > 0); $proc->close(); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/ctor.phpt b/tests/swoole_process/ctor.phpt new file mode 100644 index 00000000000..dc117b50326 --- /dev/null +++ b/tests/swoole_process/ctor.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_process: ctor +--SKIPIF-- + +--FILE-- + +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/daemon.phpt b/tests/swoole_process/daemon.phpt index dc8acc8279e..9aff59a3ec2 100644 --- a/tests/swoole_process/daemon.phpt +++ b/tests/swoole_process/daemon.phpt @@ -12,7 +12,7 @@ use Swoole\Process; $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); -$process = new Process(function (swoole_process $worker) use ($sockets) { +$process = new Process(function (Swoole\Process $worker) use ($sockets) { fclose($sockets[1]); Process::daemon(1, 1, [null, $sockets[0], $sockets[0]]); diff --git a/tests/swoole_process/deamon.phpt b/tests/swoole_process/deamon.phpt new file mode 100644 index 00000000000..dd47d927733 --- /dev/null +++ b/tests/swoole_process/deamon.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_process: deamon +--SKIPIF-- + +--FILE-- +push(posix_getpid()); +}); +$proc->useQueue(); +$forkPid = $proc->start(); +$demonPid = intval($proc->pop()); + +Assert::assert($forkPid !== $demonPid); + +\Swoole\Process::kill($demonPid, SIGKILL); + +\Swoole\Process::wait(true); +\Swoole\Process::wait(true); +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/exception.phpt b/tests/swoole_process/exception.phpt index 5e5e1895931..dc483fc7dda 100644 --- a/tests/swoole_process/exception.phpt +++ b/tests/swoole_process/exception.phpt @@ -15,7 +15,7 @@ abstract class AbstractProcess public function start() { $process = new Swoole\Process(function (Swoole\Process $process) { - swoole_event_add($process->pipe, function (Swoole\Process $process) { }); + Swoole\Event::add($process->pipe, function (Swoole\Process $process) { }); try { $this->run(); } catch (Throwable $e) { @@ -46,7 +46,7 @@ class Process6 extends AbstractProcess Fatal error: Uncaught Error: Call to undefined function AAAA() in %s:%d Stack trace: #0 %s(%d): Process6->run() -#1 [internal function]: AbstractProcess->{closure}(Object(Swoole\Process)) +#1 [internal function]: AbstractProcess->{closure%S}(Object(Swoole\Process)) #2 %s(%d): Swoole\Process->start() #3 %s(%d): AbstractProcess->start() #4 {main} diff --git a/tests/swoole_process/exec.phpt b/tests/swoole_process/exec.phpt new file mode 100644 index 00000000000..1451a4b9cfb --- /dev/null +++ b/tests/swoole_process/exec.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_process: exec +--SKIPIF-- + +--FILE-- +exec("/usr/bin/printf", ["HELLO"]); +}, true); +$proc->start(); +echo $proc->read(); +$proc->exec("/usr/bin/printf", [" WORLD"]); + +\Swoole\Process::wait(true); +?> +--EXPECT-- +HELLO WORLD diff --git a/tests/swoole_process/exit.phpt b/tests/swoole_process/exit.phpt new file mode 100644 index 00000000000..d49bd93502f --- /dev/null +++ b/tests/swoole_process/exit.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: exit +--SKIPIF-- + +--FILE-- +exit(9); +}); +$pid = $proc->start(); + +$i = \Swoole\Process::wait(true); +Assert::same($i["code"], 9); + +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/freeQueue.phpt b/tests/swoole_process/freeQueue.phpt new file mode 100644 index 00000000000..67c179fb6c3 --- /dev/null +++ b/tests/swoole_process/freeQueue.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: freeQueue +--SKIPIF-- + +--FILE-- +useQueue(); +Assert::assert($r); + +$proc->start(); +$r = $proc->freeQueue(); +Assert::assert($r); + +\Swoole\Process::wait(); + +?> +--EXPECT-- diff --git a/tests/swoole_process/getaffinity.phpt b/tests/swoole_process/getaffinity.phpt new file mode 100644 index 00000000000..e467289658d --- /dev/null +++ b/tests/swoole_process/getaffinity.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: getAffinity +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_process/kill.phpt b/tests/swoole_process/kill.phpt new file mode 100644 index 00000000000..b8b1cb5a7ed --- /dev/null +++ b/tests/swoole_process/kill.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_process: kill +--SKIPIF-- + +--FILE-- +start(); +Swoole\Process::kill($pid, SIGKILL); +$i = \Swoole\Process::wait(true); +Assert::same($i["signal"], SIGKILL); +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/msgq_capacity.phpt b/tests/swoole_process/msgq_capacity.phpt index dbf50cb1572..646f458262e 100644 --- a/tests/swoole_process/msgq_capacity.phpt +++ b/tests/swoole_process/msgq_capacity.phpt @@ -10,11 +10,11 @@ skip_if_darwin(); require __DIR__.'/../include/bootstrap.php'; -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { } -$process = new swoole_process('callback_function', false, false); +$process = new Swoole\Process('callback_function', false, false); $process->useQueue(ftok(__DIR__, 1), 1, 1024 * 1024 * 64); const N = 32 * 1024 * 1024; diff --git a/tests/swoole_process/name.phpt b/tests/swoole_process/name.phpt new file mode 100644 index 00000000000..1888ddd274b --- /dev/null +++ b/tests/swoole_process/name.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_process: name +--SKIPIF-- + +--FILE-- +name($name); + sleep(PHP_INT_MAX); +}); + +$pid = $proc->start(); +$count = (int)trim(`ps aux|grep $name|grep -v grep|wc -l`); +Assert::same($count, 1); +\Swoole\Process::kill($pid, SIGKILL); + +\Swoole\Process::wait(true); +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/null_callback.phpt b/tests/swoole_process/null_callback.phpt index dfdc67f8ac6..faa89a89ff1 100644 --- a/tests/swoole_process/null_callback.phpt +++ b/tests/swoole_process/null_callback.phpt @@ -14,4 +14,4 @@ $process->start(); ?> --EXPECTF-- -Fatal error: Swoole\Process::start(): Illegal callback function of Swoole\Process in %s +Warning: Swoole\Process::start(): illegal callback function in %s diff --git a/tests/swoole_process/pop.phpt b/tests/swoole_process/pop.phpt new file mode 100644 index 00000000000..e5dd021f4a2 --- /dev/null +++ b/tests/swoole_process/pop.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_process: pop +--SKIPIF-- + +--FILE-- +useQueue(); +$proc->push("SUCCESS"); +echo $proc->pop(); +$proc->freeQueue(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/priority_error.phpt b/tests/swoole_process/priority_error.phpt new file mode 100644 index 00000000000..a96ed68df6e --- /dev/null +++ b/tests/swoole_process/priority_error.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_process: priority [2] +--SKIPIF-- + +--FILE-- +getPriority(-1000, posix_getpid()), false); +Assert::eq(swoole_last_error(), SOCKET_EINVAL); + +Assert::eq($process->setPriority(-1000, posix_getpid(), PRIORITY), false); +Assert::eq(swoole_last_error(), SOCKET_EINVAL); + +Assert::eq(@$process->getPriority(PRIO_USER, null), false); +Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +Assert::eq(@$process->setPriority(PRIO_USER, PRIORITY, null), false); +Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +?> +--EXPECT-- diff --git a/tests/swoole_process/process_exec.phpt b/tests/swoole_process/process_exec.phpt index 64a49920cf5..ab67d1eb303 100644 --- a/tests/swoole_process/process_exec.phpt +++ b/tests/swoole_process/process_exec.phpt @@ -11,10 +11,10 @@ if (!@file_exists('/usr/bin/python')) { start(); -function python_process(swoole_process $worker) +function python_process(Swoole\Process $worker) { $worker->exec('/usr/bin/python', array(__DIR__ . "/echo.py")); } diff --git a/tests/swoole_process/process_msgqueue.phpt b/tests/swoole_process/process_msgqueue.phpt index b3e2f3613c5..3094cc2b7dc 100644 --- a/tests/swoole_process/process_msgqueue.phpt +++ b/tests/swoole_process/process_msgqueue.phpt @@ -6,9 +6,9 @@ swoole_process: sysv msgqueue useQueue(); $bytes = 0; diff --git a/tests/swoole_process/process_select.phpt b/tests/swoole_process/process_select.phpt index 0b5210ad258..dcb420e43d2 100644 --- a/tests/swoole_process/process_select.phpt +++ b/tests/swoole_process/process_select.phpt @@ -6,7 +6,7 @@ swoole_process: select write("hello master\n"); $worker->exit(0); diff --git a/tests/swoole_process/push.phpt b/tests/swoole_process/push.phpt new file mode 100644 index 00000000000..02c7df655d5 --- /dev/null +++ b/tests/swoole_process/push.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_process: push +--SKIPIF-- + +--FILE-- +useQueue(); +//$r = $proc->push("\0"); +// Assert::false($r); +// TODO max data ? +// $r = $proc->push(str_repeat("\0", 1024 * 1024 * 8)); +// Assert::false($r); +//$proc->freeQueue(); + +$proc = new Swoole\Process(function() {}); +$proc->useQueue(); +$proc->start(); +$r = $proc->push("\0"); +Assert::true($r); +$proc->freeQueue(); +\Swoole\Process::wait(true); +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/read.phpt b/tests/swoole_process/read.phpt new file mode 100644 index 00000000000..274fccaabb2 --- /dev/null +++ b/tests/swoole_process/read.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_process: read +--SKIPIF-- + +--FILE-- +write("SUCCESS"); + Assert::same($r, 7); +}); +$r = $proc->start(); +Assert::assert($r > 0); + +Swoole\Timer::after(10, function() use($proc) { + echo $proc->read(); + // Swoole\Event::exit(); +}); + +\Swoole\Process::wait(true); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/redirect.phpt b/tests/swoole_process/redirect.phpt new file mode 100644 index 00000000000..113d0076c93 --- /dev/null +++ b/tests/swoole_process/redirect.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: redirect +--SKIPIF-- + +--FILE-- +start(); +$r = $proc->read(); +echo "READ: $r~"; + +\Swoole\Process::wait(true); +?> +--EXPECT-- +READ: SUCCESS~ diff --git a/tests/swoole_process/setaffinity.phpt b/tests/swoole_process/setaffinity.phpt new file mode 100644 index 00000000000..7493e1f315b --- /dev/null +++ b/tests/swoole_process/setaffinity.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: setAffinity +--SKIPIF-- + +--FILE-- + 1) { + $r = Swoole\Process::setaffinity([0, 1]); + Assert::assert($r); +} +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index 8cdc27375c3..74ffbaaffe9 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -7,13 +7,13 @@ swoole_process: signal in manager require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; use Swoole\Server; -use function Swoole\Coroutine\run; -define('PID_FILE', __DIR__.'/manager.pid'); +const PID_FILE = __DIR__ . '/manager.pid'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { + usleep(100000); $manager_pid = file_get_contents(PID_FILE); Process::kill($manager_pid, SIGINT); $pm->wait(); @@ -21,17 +21,20 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); - $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); - $serv->on("ManagerStart", function (Server $serv) use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + ]); + $serv->on('ManagerStart', function (Server $serv) use ($pm) { file_put_contents(PID_FILE, $serv->getManagerPid()); - $pm->wakeup(); - Process::signal(SIGINT, function () use($pm) { - echo "SIGINT triggered\n"; - $pm->wakeup(); + Process::signal(SIGINT, function () use ($pm) { + echo "SIGINT triggered\n"; + $pm->wakeup(); }); + $pm->wakeup(); }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { + $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) { }); $serv->start(); }; diff --git a/tests/swoole_process/signal_in_task_worker.phpt b/tests/swoole_process/signal_in_task_worker.phpt index e174e8355af..58972efad7c 100644 --- a/tests/swoole_process/signal_in_task_worker.phpt +++ b/tests/swoole_process/signal_in_task_worker.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'task_worker_num' => 1, @@ -32,11 +32,11 @@ $pm->childFunc = function () use ($pm) { $serv->on("WorkerStart", function (Server $serv) use ($pm) { if ($serv->taskworker) { file_put_contents(PID_FILE, $serv->getWorkerPid()); - $pm->wakeup(); Process::signal(SIGINT, function () use($pm) { echo "SIGINT triggered\n"; $pm->wakeup(); }); + $pm->wakeup(); } }); $serv->on("Task", function (Server $serv) use ($pm) { diff --git a/tests/swoole_process/start.phpt b/tests/swoole_process/start.phpt new file mode 100644 index 00000000000..b4477ce40f6 --- /dev/null +++ b/tests/swoole_process/start.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_process: start +--SKIPIF-- + +--FILE-- +start(); +Assert::assert($r > 0); +$proc->close(); + +\Swoole\Process::wait(true); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/swoole_process_close.phpt b/tests/swoole_process/swoole_process_close.phpt deleted file mode 100644 index 0e05d1d5c15..00000000000 --- a/tests/swoole_process/swoole_process_close.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_process: close ---SKIPIF-- - ---FILE-- -close(); -//}); -//$proc->start(); -// -//$proc = new \swoole_process(swoole_function(\swoole_process $proc) { -// usleep(200000); -// // Assert::true(false, 'never here'); -//}); -//$proc->start(); -//$proc->close(); -// -// -//\swoole_process::wait(true); -//\swoole_process::wait(true); -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_ctor.phpt b/tests/swoole_process/swoole_process_ctor.phpt deleted file mode 100644 index 3f84b80a7c7..00000000000 --- a/tests/swoole_process/swoole_process_ctor.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_process: ctor ---SKIPIF-- - ---FILE-- - ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_deamon.phpt b/tests/swoole_process/swoole_process_deamon.phpt deleted file mode 100644 index c70daf42860..00000000000 --- a/tests/swoole_process/swoole_process_deamon.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_process: deamon ---SKIPIF-- - ---FILE-- -push(posix_getpid()); -}); -$proc->useQueue(); -$forkPid = $proc->start(); -$demonPid = intval($proc->pop()); - -Assert::assert($forkPid !== $demonPid); - -\swoole_process::kill($demonPid, SIGKILL); - -\swoole_process::wait(true); -\swoole_process::wait(true); -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_exec.phpt b/tests/swoole_process/swoole_process_exec.phpt deleted file mode 100644 index 20ddada2968..00000000000 --- a/tests/swoole_process/swoole_process_exec.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_process: exec ---SKIPIF-- - ---FILE-- -exec("/usr/bin/printf", ["HELLO"]); -}, true); -$proc->start(); -echo $proc->read(); -$proc->exec("/usr/bin/printf", [" WORLD"]); - -\swoole_process::wait(true); -?> ---EXPECT-- -HELLO WORLD diff --git a/tests/swoole_process/swoole_process_exit.phpt b/tests/swoole_process/swoole_process_exit.phpt deleted file mode 100644 index f9bc8f8e44a..00000000000 --- a/tests/swoole_process/swoole_process_exit.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_process: exit ---SKIPIF-- - ---FILE-- -exit(9); -}); -$pid = $proc->start(); - -$i = \swoole_process::wait(true); -Assert::same($i["code"], 9); - -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_freeQueue.phpt b/tests/swoole_process/swoole_process_freeQueue.phpt deleted file mode 100644 index d01003068af..00000000000 --- a/tests/swoole_process/swoole_process_freeQueue.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_process: freeQueue ---SKIPIF-- - ---FILE-- -useQueue(); -Assert::assert($r); - -$proc->start(); -$r = $proc->freeQueue(); -Assert::assert($r); - -\swoole_process::wait(); - -?> ---EXPECT-- diff --git a/tests/swoole_process/swoole_process_kill.phpt b/tests/swoole_process/swoole_process_kill.phpt deleted file mode 100644 index 91a6920f2ad..00000000000 --- a/tests/swoole_process/swoole_process_kill.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_process: kill ---SKIPIF-- - ---FILE-- -start(); -swoole_process::kill($pid, SIGKILL); -$i = \swoole_process::wait(true); -Assert::same($i["signal"], SIGKILL); -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_name.phpt b/tests/swoole_process/swoole_process_name.phpt deleted file mode 100644 index de8e4a9b639..00000000000 --- a/tests/swoole_process/swoole_process_name.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_process: name ---SKIPIF-- - ---FILE-- -name($name); - sleep(PHP_INT_MAX); -}); - -$pid = $proc->start(); -$count = (int)trim(`ps aux|grep $name|grep -v grep|wc -l`); -Assert::same($count, 1); -\swoole_process::kill($pid, SIGKILL); - -\swoole_process::wait(true); -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_pop.phpt b/tests/swoole_process/swoole_process_pop.phpt deleted file mode 100644 index 92221ce2496..00000000000 --- a/tests/swoole_process/swoole_process_pop.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_process: pop ---SKIPIF-- - ---FILE-- -useQueue(); -$proc->push("SUCCESS"); -echo $proc->pop(); -$proc->freeQueue(); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_push.phpt b/tests/swoole_process/swoole_process_push.phpt deleted file mode 100644 index a678602a56b..00000000000 --- a/tests/swoole_process/swoole_process_push.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_process: push ---SKIPIF-- - ---FILE-- -useQueue(); -//$r = $proc->push("\0"); -// Assert::false($r); -// TODO max data ? -// $r = $proc->push(str_repeat("\0", 1024 * 1024 * 8)); -// Assert::false($r); -//$proc->freeQueue(); - -$proc = new \swoole_process(function() {}); -$proc->useQueue(); -$proc->start(); -$r = $proc->push("\0"); -Assert::true($r); -$proc->freeQueue(); -\swoole_process::wait(true); -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_read.phpt b/tests/swoole_process/swoole_process_read.phpt deleted file mode 100644 index 7688fbd1068..00000000000 --- a/tests/swoole_process/swoole_process_read.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_process: read ---SKIPIF-- - ---FILE-- -write("SUCCESS"); - Assert::same($r, 7); -}); -$r = $proc->start(); -Assert::assert($r > 0); - -swoole_timer_after(10, function() use($proc) { - echo $proc->read(); - // swoole_event_exit(); -}); - -\swoole_process::wait(true); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_redirect.phpt b/tests/swoole_process/swoole_process_redirect.phpt deleted file mode 100644 index 1aadb505f63..00000000000 --- a/tests/swoole_process/swoole_process_redirect.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_process: redirect ---SKIPIF-- - ---FILE-- -start(); -$r = $proc->read(); -echo "READ: $r~"; - -\swoole_process::wait(true); -?> ---EXPECT-- -READ: SUCCESS~ diff --git a/tests/swoole_process/swoole_process_setaffinity.phpt b/tests/swoole_process/swoole_process_setaffinity.phpt deleted file mode 100644 index f69f30486e9..00000000000 --- a/tests/swoole_process/swoole_process_setaffinity.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_process: setaffinity ---SKIPIF-- - ---FILE-- - 1) { - $r = Swoole\Process::setaffinity([0, 1]); - Assert::assert($r); -} -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_start.phpt b/tests/swoole_process/swoole_process_start.phpt deleted file mode 100644 index 76071782c38..00000000000 --- a/tests/swoole_process/swoole_process_start.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_process: start ---SKIPIF-- - ---FILE-- -start(); -Assert::assert($r > 0); -$proc->close(); - -\swoole_process::wait(true); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_useQueue.phpt b/tests/swoole_process/swoole_process_useQueue.phpt deleted file mode 100644 index 98c8e5afca5..00000000000 --- a/tests/swoole_process/swoole_process_useQueue.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_process: useQueue ---SKIPIF-- - ---FILE-- -pop(); -}); -$proc->useQueue(); -$proc->start(); -$proc->push("SUCCESS"); - -\swoole_process::wait(true); -$proc->freeQueue(); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_wait.phpt b/tests/swoole_process/swoole_process_wait.phpt deleted file mode 100644 index a78126ec7ef..00000000000 --- a/tests/swoole_process/swoole_process_wait.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_process: wait ---SKIPIF-- - ---FILE-- -start(); -$info = \swoole_process::wait(true); -Assert::same($pid, $info["pid"]); -Assert::same($info["code"], 0); -Assert::same($info["signal"], 0); - -$proc = new \swoole_process(function() { exit(1); }); -$pid = $proc->start(); -$info = \swoole_process::wait(true); -Assert::same($pid, $info["pid"]); -Assert::same($info["code"], 1); -Assert::same($info["signal"], 0); - -$proc = new \swoole_process(function() { \swoole_process::kill(posix_getpid(), SIGTERM); }); -$pid = $proc->start(); -$info = \swoole_process::wait(true); -Assert::same($pid, $info["pid"]); -Assert::same($info["code"], 0); -Assert::same($info["signal"], SIGTERM); - -echo "SUCCESS"; -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/swoole_process_write.phpt b/tests/swoole_process/swoole_process_write.phpt deleted file mode 100644 index e6d48e0f953..00000000000 --- a/tests/swoole_process/swoole_process_write.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_process: write ---SKIPIF-- - ---FILE-- -write("SUCCESS"); - Assert::same($r, 7); -}); -$r = $proc->start(); -Assert::assert($r > 0); - -swoole_timer_after(10, function() use($proc) { - echo $proc->read(); -}); - -\swoole_process::wait(true); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_process/timeout.phpt b/tests/swoole_process/timeout.phpt index 15fd0129d1a..8cd3236d7e5 100644 --- a/tests/swoole_process/timeout.phpt +++ b/tests/swoole_process/timeout.phpt @@ -6,7 +6,7 @@ swoole_process: pipe read timeout start(); @@ -15,7 +15,7 @@ ini_set("swoole.display_errors", "off"); $proc->setTimeout(0.5); $ret = $proc->read(); Assert::false($ret); -swoole_process::kill($proc->pid, SIGKILL); -\swoole_process::wait(true); +Swoole\Process::kill($proc->pid, SIGKILL); +\Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/useQueue.phpt b/tests/swoole_process/useQueue.phpt new file mode 100644 index 00000000000..05be5a2e530 --- /dev/null +++ b/tests/swoole_process/useQueue.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: useQueue +--SKIPIF-- + +--FILE-- +pop(); +}); +$proc->useQueue(); +$proc->start(); +$proc->push("SUCCESS"); + +\Swoole\Process::wait(true); +$proc->freeQueue(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/wait.phpt b/tests/swoole_process/wait.phpt new file mode 100644 index 00000000000..2f7249a9147 --- /dev/null +++ b/tests/swoole_process/wait.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_process: wait +--SKIPIF-- + +--FILE-- +start(); +$info = \Swoole\Process::wait(true); +Assert::same($pid, $info["pid"]); +Assert::same($info["code"], 0); +Assert::same($info["signal"], 0); + +$proc = new Swoole\Process(function() { exit(1); }); +$pid = $proc->start(); +$info = \Swoole\Process::wait(true); +Assert::same($pid, $info["pid"]); +Assert::same($info["code"], 1); +Assert::same($info["signal"], 0); + +$proc = new Swoole\Process(function() { \Swoole\Process::kill(posix_getpid(), SIGTERM); }); +$pid = $proc->start(); +$info = \Swoole\Process::wait(true); +Assert::same($pid, $info["pid"]); +Assert::same($info["code"], 0); +Assert::same($info["signal"], SIGTERM); + +echo "SUCCESS"; +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/wait_signal.phpt b/tests/swoole_process/wait_signal.phpt index ed497d8dd02..f036f6d9ef6 100644 --- a/tests/swoole_process/wait_signal.phpt +++ b/tests/swoole_process/wait_signal.phpt @@ -9,6 +9,9 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; use Swoole\Event; +ini_set('serialize_precision', -1); +ini_set('precision', -1); + $proc = new Process(function(Process $process) { swoole_async_set(['wait_signal' => true]); Process::signal(SIGINT, function () { diff --git a/tests/swoole_process/write.phpt b/tests/swoole_process/write.phpt new file mode 100644 index 00000000000..598aaf711b5 --- /dev/null +++ b/tests/swoole_process/write.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_process: write +--SKIPIF-- + +--FILE-- +write("SUCCESS"); + Assert::same($r, 7); +}); +$r = $proc->start(); +Assert::assert($r > 0); + +Swoole\Timer::after(10, function() use($proc) { + echo $proc->read(); +}); + +\Swoole\Process::wait(true); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_process/write_in_worker.phpt b/tests/swoole_process/write_in_worker.phpt index 5068a7e49a9..b6bc2264772 100644 --- a/tests/swoole_process/write_in_worker.phpt +++ b/tests/swoole_process/write_in_worker.phpt @@ -11,8 +11,8 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm, $counter) { - $serv = new \swoole_server('127.0.0.1', $pm->getFreePort()); - $process = new \swoole_process(function (swoole_process $process) use ($serv, $counter) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $process = new Swoole\Process(function (Swoole\Process $process) use ($serv, $counter) { if ($counter->get() != 1) { $counter->set(1); echo "process start\n"; @@ -27,7 +27,7 @@ $pm->childFunc = function () use ($pm, $counter) { "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($process, $pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($process, $pm) { usleep(1); for ($i = 0; $i < 1024; $i++) { Assert::same($process->write(str_repeat('A', 8192)), 8192); diff --git a/tests/swoole_process_pool/create_websocket_server.phpt b/tests/swoole_process_pool/create_websocket_server.phpt index eff7754ef23..140aefda44c 100644 --- a/tests/swoole_process_pool/create_websocket_server.phpt +++ b/tests/swoole_process_pool/create_websocket_server.phpt @@ -18,7 +18,7 @@ $pool = new Swoole\Process\Pool($workerNum); $pool->on("WorkerStart", function ($pool, $workerId) use ($port) { if ($workerId === 0) { - $server = new Server('127.0.0.1', $port); + $server = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $server->on("message", function ($server, $frame) { $server->push($frame->fd, $frame->data); }); @@ -40,5 +40,4 @@ $pool->start(); echo "DONE\n"; ?> --EXPECTF-- -[%s] INFO Server is shutdown now DONE diff --git a/tests/swoole_process_pool/detach.phpt b/tests/swoole_process_pool/detach.phpt index a7b92ef52ba..62a13a4495b 100644 --- a/tests/swoole_process_pool/detach.phpt +++ b/tests/swoole_process_pool/detach.phpt @@ -19,8 +19,8 @@ $pm->initFreePorts(); $pm->parentFunc = function ($pid) use ($pm, $atomic) { foreach (range(1, 2) as $i) { - $fp = stream_socket_client("tcp://127.0.0.1:".$pm->getFreePort(), $errno, $errstr) or die("error: $errstr\n"); - $msg = "HELLO-{$i}"; + $fp = stream_socket_client("tcp://127.0.0.1:" . $pm->getFreePort(), $errno, $errstr) or die("error: $errstr\n"); + $msg = "HELLO-{$i}"; fwrite($fp, pack('N', strlen($msg)) . $msg); } $pm->wait(); @@ -32,25 +32,25 @@ $pm->parentFunc = function ($pid) use ($pm, $atomic) { $pm->childFunc = function () use ($pm, $atomic) { $pool = new Pool(1, SWOOLE_IPC_SOCKET); - $pool->on('WorkerStart', function (Pool $pool, $workerId) use($pm, $atomic) { + $pool->on('WorkerStart', function (Pool $pool, $workerId) use ($pm, $atomic) { echo("[Worker #{$workerId}] WorkerStart\n"); - if ($atomic->get() == 0) { - $pm->wakeup(); - } + if ($atomic->get() == 0) { + $pm->wakeup(); + } }); - $pool->on('Message', function (Pool $pool, $msg) use($pm, $atomic) { + $pool->on('Message', function (Pool $pool, $msg) use ($pm, $atomic) { if ($atomic->get() == 0) { $atomic->add(); $pool->detach(); $n = N; - while($n--) { + while ($n--) { usleep(1000); $atomic->add(); } $pm->wakeup(); } else { - echo $msg.PHP_EOL; + echo $msg . PHP_EOL; } }); diff --git a/tests/swoole_process_pool/getprocess_3.phpt b/tests/swoole_process_pool/getprocess_3.phpt index daed3045e31..b66cc57694a 100644 --- a/tests/swoole_process_pool/getprocess_3.phpt +++ b/tests/swoole_process_pool/getprocess_3.phpt @@ -14,7 +14,7 @@ const N = 70000; $pool = new Pool(2, SWOOLE_IPC_UNIXSOCK); -$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { +$pool->on('workerStart', function (Pool $pool, int $workerId) { if ($workerId == 0) { usleep(1000); $process1 = $pool->getProcess(1); diff --git a/tests/swoole_process_pool/getprocess_4.phpt b/tests/swoole_process_pool/getprocess_4.phpt new file mode 100644 index 00000000000..920c913957d --- /dev/null +++ b/tests/swoole_process_pool/getprocess_4.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_process_pool: get process 4 [async] +--SKIPIF-- + +--FILE-- +set(['enable_coroutine' => true]); + +$pool->on('workerStart', function (Pool $pool, int $workerId) { + if ($workerId == 0) { + usleep(1000); + $process1 = $pool->getProcess(1); + phpt_var_dump($process1); + $pid1 = $process1->pid; + Process::kill($process1->pid, SIGTERM); + usleep(100000); + $process2 = $pool->getProcess(1); + phpt_var_dump($process2); + $pid2 = $process2->pid; + Assert::notEq($pid1, $pid2); + $pool->shutdown(); + } +}); + +$pool->on("message", function ($pool, $data) { + +}); + +$pool->start(); + +?> +--EXPECT-- diff --git a/tests/swoole_process_pool/getprocess_5.phpt b/tests/swoole_process_pool/getprocess_5.phpt new file mode 100644 index 00000000000..69bbb9fe8fd --- /dev/null +++ b/tests/swoole_process_pool/getprocess_5.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_process_pool: getProcess [5] +--SKIPIF-- + +--FILE-- +on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) { + if ($workerId == 0) { + $process1 = $pool->getProcess(); + $process2 = $pool->getProcess(1); + $process2->write(str_repeat('A', N)); + Assert::same(@$pool->getProcess(2), false); + + if ($process1->read() == 'shutdown') { + $pool->shutdown(); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) { + Assert::length($data, N); + $process1 = $pool->getProcess(0); + $process1->write("shutdown"); +}); + +$pool->start(); +?> +--EXPECT-- diff --git a/tests/swoole_process_pool/master_callback.phpt b/tests/swoole_process_pool/master_callback.phpt new file mode 100644 index 00000000000..0bfb29f95ca --- /dev/null +++ b/tests/swoole_process_pool/master_callback.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: master callback +--SKIPIF-- + +--FILE-- +on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + echo "worker start\n"; + Assert::true($pool->workerRunning); + Assert::eq($pool->workerId, 0); + Assert::eq($pool->workerPid, posix_getpid()); + pcntl_signal(SIGTERM, function (){ + + }); + $pool->shutdown(); + sleep(20); + echo "worker exit\n"; +}); + +$pool->on('workerStop', function (Swoole\Process\Pool $pool, int $workerId) { + Assert::false($pool->workerRunning); + echo "worker stop\n"; +}); + +$pool->on('start', function (Swoole\Process\Pool $pool) { + Assert::true($pool->running); + echo "start\n"; +}); + +$pool->on('shutdown', function (Swoole\Process\Pool $pool) { + Assert::false($pool->running); + echo "shutdown\n"; +}); + +$pool->start(); +?> +--EXPECT-- +start +worker start +shutdown +worker exit +worker stop diff --git a/tests/swoole_process_pool/max_wait_time.phpt b/tests/swoole_process_pool/max_wait_time.phpt new file mode 100644 index 00000000000..b437dddc551 --- /dev/null +++ b/tests/swoole_process_pool/max_wait_time.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_process_pool: max wait time +--SKIPIF-- + +--FILE-- +set([ + Constant::OPTION_ENABLE_COROUTINE => true, + Constant::OPTION_MAX_WAIT_TIME => 1, + ]); + + $pool->on('workerStart', function (Pool $pool, int $workerId) use ($atomic): void { + echo "workerStart: $workerId" . PHP_EOL; + $atomic->wait(-1); + }); + + $pool->on('start', function () use ($pool): void { + Timer::after(500, function () use ($pool): void { + $pool->shutdown(); + }); + echo 'start' . PHP_EOL; + }); + + $pool->on('shutdown', function () use ($atomic): void { + echo 'shutdown' . PHP_EOL; + }); + + $pool->start(); +})(); +?> +--EXPECTF-- +start +workerStart: %d +workerStart: %d +workerStart: %d +workerStart: %d +shutdown diff --git a/tests/swoole_process_pool/message.phpt b/tests/swoole_process_pool/message.phpt index 1ed86cfa54c..671a99af4f3 100644 --- a/tests/swoole_process_pool/message.phpt +++ b/tests/swoole_process_pool/message.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', 8089, 5)); $data = "hello swoole!"; $client->send(pack('N', strlen($data)) . $data); @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $pool = new Swoole\Process\Pool(1, SWOOLE_IPC_SOCKET); $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', 8089, 5)); $data = "hello swoole! (from workerStart)"; $client->send(pack('N', strlen($data)) . $data); diff --git a/tests/swoole_process_pool/message_async.phpt b/tests/swoole_process_pool/message_async.phpt new file mode 100644 index 00000000000..2b3caecacc3 --- /dev/null +++ b/tests/swoole_process_pool/message_async.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: message async [disable message bus] +--SKIPIF-- + +--FILE-- +set([ + 'enable_coroutine' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + System::sleep(0.002); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/message_bus.phpt b/tests/swoole_process_pool/message_bus.phpt new file mode 100644 index 00000000000..1adbccd7178 --- /dev/null +++ b/tests/swoole_process_pool/message_bus.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: message bus +--SKIPIF-- + +--FILE-- +set([ + 'enable_coroutine' => true, + 'enable_message_bus' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + Co::sleep(0.002); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/message_bus_sync.phpt b/tests/swoole_process_pool/message_bus_sync.phpt new file mode 100644 index 00000000000..2d1369125c4 --- /dev/null +++ b/tests/swoole_process_pool/message_bus_sync.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_process_pool: message bus [sync] +--SKIPIF-- + +--FILE-- +set([ + 'enable_message_bus' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + usleep(2000); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/msgqueue.phpt b/tests/swoole_process_pool/msgqueue.phpt index 2f58e9416f9..fe1dd64fba1 100644 --- a/tests/swoole_process_pool/msgqueue.phpt +++ b/tests/swoole_process_pool/msgqueue.phpt @@ -10,20 +10,23 @@ if (function_exists('msg_get_queue') == false) { parentFunc = function ($pid) use ($pm) { +$pm->parentFunc = function ($pid) use ($pm, $atomic) { $seg = msg_get_queue(MSGQ_KEY); - foreach (range(1, 100) as $i) { + foreach (range(1, N) as $i) { $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]); msg_send($seg, $i, $data, false); } - $pm->kill(); }; -$pm->childFunc = function () use ($pm) { +$pm->childFunc = function () use ($pm, $atomic) { $pool = new Swoole\Process\Pool(1, SWOOLE_IPC_MSGQUEUE, MSGQ_KEY); $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) { @@ -31,11 +34,16 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $pool->on("message", function (Swoole\Process\Pool $pool, string $message) { + $pool->on("message", function (Swoole\Process\Pool $pool, string $message) use ($atomic) { $data = json_decode($message, true); Assert::assert($data); Assert::assert(is_array($data)); Assert::same(strlen(base64_decode($data['data'])), 1024); + $atomic->add(1); + if ($atomic->get() == 100) { + $pool->shutdown(); + echo "DONE\n"; + } }); $pool->on('workerStop', function (Swoole\Process\Pool $pool, int $workerId) { @@ -51,4 +59,5 @@ $pm->run(); ?> --EXPECT-- worker start +DONE worker stop diff --git a/tests/swoole_process_pool/msgqueue_2.phpt b/tests/swoole_process_pool/msgqueue_2.phpt new file mode 100644 index 00000000000..7df902db48b --- /dev/null +++ b/tests/swoole_process_pool/msgqueue_2.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_process_pool: sysv msgqueue [2] +--SKIPIF-- + +--FILE-- +on('workerStart', function (Pool $pool, int $workerId) { + if ($workerId == 0) { + echo "worker start\n"; + Assert::true($pool->getProcess()->push('hello world' . PHP_EOL)); + } else { + echo $pool->getProcess()->pop(); + $pool->shutdown(); + } +}); + +$pool->on('workerStop', function (Pool $pool, int $workerId) { + if ($workerId == 1) { + echo "worker stop\n"; + } +}); + +$pool->start(); +?> +--EXPECT-- +worker start +hello world +worker stop diff --git a/tests/swoole_process_pool/reload.phpt b/tests/swoole_process_pool/reload.phpt index 91caab6d7cf..2a68f28e050 100644 --- a/tests/swoole_process_pool/reload.phpt +++ b/tests/swoole_process_pool/reload.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_process_pool: sysv msgqueue +swoole_process_pool: reload --SKIPIF-- parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < 5; $i++) - { - swoole_process::kill($pid, SIGUSR1); + for ($i = 0; $i < 5; $i++) { + Swoole\Process::kill($pid, SIGUSR1); usleep(10000); //判断进程是否存在 Assert::assert(intval(shell_exec("ps aux | grep \"" . PROC_NAME . "\" |grep -v grep| awk '{ print $2}'")) > 0); @@ -25,23 +24,21 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - swoole_set_process_name(PROC_NAME); + cli_set_process_title(PROC_NAME); Co::set(['log_level' => SWOOLE_LOG_ERROR]); $pool = new Swoole\Process\Pool(2); - $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) - { + $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) { $pm->wakeup(); - swoole_timer::tick(1000, function () use ($workerId) - { + Swoole\Timer::tick(1000, function () use ($workerId) { echo "sleep [$workerId] \n"; }); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_event_wait(); + Swoole\Event::wait(); }); $pool->start(); diff --git a/tests/swoole_process_pool/socket_coro.phpt b/tests/swoole_process_pool/socket_coro.phpt index 19bb969b37a..dab8d9b26fb 100644 --- a/tests/swoole_process_pool/socket_coro.phpt +++ b/tests/swoole_process_pool/socket_coro.phpt @@ -7,10 +7,12 @@ swoole_process_pool: co\socket childFunc = function () use ($pm) { $socket = new Socket(AF_INET, SOCK_STREAM, 0); $socket->bind('127.0.0.1', $pm->getFreePort()); - $atomic = new \Swoole\Atomic(); + $atomic = new Atomic(); - $pool = new Swoole\Process\Pool(2); + $pool = new Pool(2); $pool->set(['enable_coroutine' => true]); $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($socket, $pm, $atomic) { $socket->listen(128); diff --git a/tests/swoole_process_pool/worker_exit_1.phpt b/tests/swoole_process_pool/worker_exit_1.phpt new file mode 100644 index 00000000000..2b9462eee2a --- /dev/null +++ b/tests/swoole_process_pool/worker_exit_1.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_process_pool: worker exit +--SKIPIF-- + +--FILE-- +on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + echo "worker start\n"; + Assert::eq($pool->workerId, $workerId); + + $count = 0; + while ($GLOBALS['running']) { + Co::sleep(0.03); + echo "sleep\n"; + if (++$count === 3) { + $pool->shutdown(); + } + } +}); + +$pool->on('workerStop', function ($pool, $data) { + echo "worker stop\n"; +}); + +$pool->on('workerExit', function ($pool, $data) { + $GLOBALS['count']++; + if ($GLOBALS['count'] == 3) { + $GLOBALS['running'] = false; + } + echo ('worker exit') . PHP_EOL; +}); + +$pool->start(); +?> +--EXPECT-- +worker start +sleep +sleep +sleep +worker exit +sleep +worker exit +sleep +worker exit +worker stop diff --git a/tests/swoole_redis_coro/auth.phpt b/tests/swoole_redis_coro/auth.phpt deleted file mode 100644 index c872c100022..00000000000 --- a/tests/swoole_redis_coro/auth.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis auth ---SKIPIF-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (!$redis->auth(REDIS_SERVER_PWD)) { - skip('no auth'); -} -?> ---FILE-- -getAuth()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getAuth()); - Assert::assert(!$redis->auth(get_safe_random())); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->getAuth()); - Assert::assert($redis->auth(REDIS_SERVER_PWD)); - Assert::same($redis->getAuth(), REDIS_SERVER_PWD); - // auth by connect - $redis = new Swoole\Coroutine\Redis(['password' => REDIS_SERVER_PWD]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->set('foo', $random = get_safe_random())); - Assert::same($redis->get('foo'), $random); - // auth failed when connect - $redis = new Swoole\Coroutine\Redis(['password' => get_safe_random()]); - Assert::assert(!$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect.phpt b/tests/swoole_redis_coro/auto_reconnect.phpt deleted file mode 100644 index 09f163dcf58..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - $ret = $redis->close(); - Assert::assert($ret); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - $ret = $redis->get('foo'); - Assert::same($ret, 'bar'); -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect_ex.phpt b/tests/swoole_redis_coro/auto_reconnect_ex.phpt deleted file mode 100644 index fa4dabb587f..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect_ex.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: auto reconnect after server side close the connection ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Co\run(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::true($ret); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($ret, "code: {$redis->errCode}, msg={$redis->errMsg}"); - $ret = $redis->get('random_val'); - Assert::true($ret && $ret === $random, "code: {$redis->errCode}, msg={$redis->errMsg}"); - Co::sleep(0.001); - } - $redis->setOptions(['reconnect' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($n === MAX_REQUESTS ? $ret : !$ret); - $ret = $redis->get('random_val'); - Assert::true($n === MAX_REQUESTS ? ($ret && $ret === $random) : !$ret); - Co::sleep(0.001); - } - }); - $pm->kill(); - echo "DONE\n"; -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - $server->close($fd); - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/basic.phpt b/tests/swoole_redis_coro/basic.phpt deleted file mode 100644 index 1bb414bd73e..00000000000 --- a/tests/swoole_redis_coro/basic.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (\swoole_server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/bug_lock.phpt b/tests/swoole_redis_coro/bug_lock.phpt deleted file mode 100644 index 0f90cfe8e75..00000000000 --- a/tests/swoole_redis_coro/bug_lock.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -lock('SWOOLE_TEST_LOCK')) { - echo "ERROR\n"; - $redis_lock->unlock('SWOOLE_TEST_LOCK'); - } else { - echo "FREE\n"; - } - } - SQLPool::release(); -}); - -swoole_event_wait(); -?> ---EXPECT-- -LOCK -FREE -LOCK -ERROR -LOCK -FREE diff --git a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt b/tests/swoole_redis_coro/compatibility_mode/hExists.phpt deleted file mode 100644 index d93df485443..00000000000 --- a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro/compatibility_mode: hExists ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete(KEY); - $redis->hSet(KEY, 'field', 'val1'); - - Assert::true($redis->hExists(KEY, 'field') === true); - Assert::true($redis->hExists(KEY, 'field_not_found') === false); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/connect_timeout.phpt b/tests/swoole_redis_coro/connect_timeout.phpt deleted file mode 100644 index cf49ac852cf..00000000000 --- a/tests/swoole_redis_coro/connect_timeout.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client connect timeout ---SKIPIF-- - ---FILE-- - $timeout]); - $s = microtime(true); - $ret = $redis->connect('192.0.0.1', 9000); - Assert::assert(!$ret); - Assert::assert($redis->errCode === SOCKET_ETIMEDOUT); - time_approximate($timeout, microtime(true) - $s); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_to_wrong.phpt b/tests/swoole_redis_coro/connect_to_wrong.phpt deleted file mode 100644 index ed0e245c69a..00000000000 --- a/tests/swoole_redis_coro/connect_to_wrong.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - Assert::assert(!$redis->set('foo', 'bar')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_twice-2.phpt b/tests/swoole_redis_coro/connect_twice-2.phpt deleted file mode 100644 index b8ebfc4d21f..00000000000 --- a/tests/swoole_redis_coro/connect_twice-2.phpt +++ /dev/null @@ -1,64 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (\swoole_server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res2 = @$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($res2); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/connect_twice.phpt b/tests/swoole_redis_coro/connect_twice.phpt deleted file mode 100644 index f11ca2da744..00000000000 --- a/tests/swoole_redis_coro/connect_twice.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_redis_coro: connect twice ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "connect [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [1]\n"; - $redis->close(); - Assert::false($redis->connected); - echo "connect [2]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [2]\n"; - $redis->close(); - Assert::false($redis->connected); -}); - -swoole_event::wait(); -?> ---EXPECT-- -connect [1] -close [1] -connect [2] -close [2] diff --git a/tests/swoole_redis_coro/curd.phpt b/tests/swoole_redis_coro/curd.phpt deleted file mode 100644 index 869760fc6bc..00000000000 --- a/tests/swoole_redis_coro/curd.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 0.5]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/defer.phpt b/tests/swoole_redis_coro/defer.phpt deleted file mode 100644 index fe3709e990c..00000000000 --- a/tests/swoole_redis_coro/defer.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -swoole_redis_coro: defer ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "CONNECT [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->setDefer(); - echo "SET [1]\n"; - $redis->set('key1', 'value'); - - $redis2 = new Swoole\Coroutine\Redis(); - echo "CONNECT [2]\n"; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis2->setDefer(); - echo "GET [2]\n"; - $redis2->get('key1'); - - echo "RECV [1]\n"; - $result1 = $redis->recv(); - var_dump($result1); - - echo "RECV [2]\n"; - $result2 = $redis2->recv(); - var_dump($result2); -}); - -swoole_event::wait(); -?> ---EXPECT-- -CONNECT [1] -SET [1] -CONNECT [2] -GET [2] -RECV [1] -bool(true) -RECV [2] -string(5) "value" diff --git a/tests/swoole_redis_coro/different_connect.phpt b/tests/swoole_redis_coro/different_connect.phpt deleted file mode 100644 index 51bcb46567c..00000000000 --- a/tests/swoole_redis_coro/different_connect.phpt +++ /dev/null @@ -1,47 +0,0 @@ ---TEST-- -swoole_redis_coro: connect the same target and different ---SKIPIF-- - ---FILE-- - -1]); -function test(string $host, int $port = 0) -{ - $redis = new Swoole\Coroutine\Redis(); - Assert::same($redis->sock, -1); - - $real_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $real_connect_time = microtime(true) - $real_connect_time; - - Assert::assert($ret); - Assert::assert(($fd = $redis->sock) > 0); - - $fake_connect_time = 0; - for ($n = MAX_REQUESTS; $n--;) { - $fake_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $fake_connect_time = microtime(true) - $fake_connect_time; - Assert::assert($ret); - Assert::assert($fake_connect_time < $real_connect_time); - } - - $real_connect_time = microtime(true); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - $real_connect_time = microtime(true) - $real_connect_time; - Assert::assert($fake_connect_time < $real_connect_time); - Assert::assert(!$redis->get('foo')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -} - -go('test', REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (file_exists(REDIS_SERVER_PATH)) { - go('test', 'unix:' . str_repeat('/', mt_rand(1, 3)) . REDIS_SERVER_PATH); -} - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/disable_retry.phpt b/tests/swoole_redis_coro/disable_retry.phpt deleted file mode 100644 index 6e5149a1cfe..00000000000 --- a/tests/swoole_redis_coro/disable_retry.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: disable retry ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $redis->setOptions(['retry' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - if ($n % 2) { - Assert::same($ret, $random); - } else { - Assert::assert(!$ret); - } - } - $pm->kill(); - echo "DONE\n"; - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - static $rid = 0; - if (count($data) == 0) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); - } - $key = $data[0]; - if ($rid++ % 2) { - $server->close($fd); - } else { - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_failed.phpt b/tests/swoole_redis_coro/donot_retry_after_failed.phpt deleted file mode 100644 index 826fffc9242..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_failed.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: don not retry again after connect failed ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; - -$cid = go(function () use ($sock) { - $sock->listen(); - $sock->accept(); - co::yield(); - $sock->close(); -}); - -go(function () use ($cid, $port) { - $redis = new Swoole\Coroutine\Redis(); - $ret = $redis->connect('127.0.0.1', 65535); - Assert::assert(!$ret); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->get('foo'); - Assert::assert(!$ret); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_CLOSED); - } - $ret = $redis->connect('127.0.0.1', $port); - Assert::assert($ret); - Assert::assert($redis->connected); - Assert::same($redis->errCode, 0, $redis->errCode); - Assert::same($redis->errMsg, '', $redis->errMsg); - co::sleep(0.001); - co::resume($cid); -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt b/tests/swoole_redis_coro/donot_retry_after_server_down.phpt deleted file mode 100644 index c16cf432f69..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_redis_coro: do not retry after server down ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - Assert::same($ret, $random); - $pm->kill(); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNRESET); - for ($n = MAX_REQUESTS; $n--;) { - Assert::assert(!$redis->set('random_val', get_safe_random(128))); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - } - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('workerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/err.phpt b/tests/swoole_redis_coro/err.phpt deleted file mode 100644 index 6b2ad5c4667..00000000000 --- a/tests/swoole_redis_coro/err.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_redis_coro: redis error return ---SKIPIF-- - ---FILE-- - 3]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res = $redis->set('foo', 'bar'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); - $res = $redis->hIncrBy('foo', 'bar', 123); - Assert::assert(!$res); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_OTHER); - var_dump($redis->errMsg); - $res = $redis->set('foo', 'baz'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); -}); -?> ---EXPECT-- -string(65) "WRONGTYPE Operation against a key holding the wrong kind of value" diff --git a/tests/swoole_redis_coro/getDbNum.phpt b/tests/swoole_redis_coro/getDbNum.phpt deleted file mode 100644 index 581ba0c552c..00000000000 --- a/tests/swoole_redis_coro/getDbNum.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select db ---SKIPIF-- - ---FILE-- -getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 0); - // select and success - Assert::true($redis->select(1)); - Assert::same($redis->getDBNum(), 1); - // select but failed - Assert::false($redis->select(-1)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->select(1001)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::same($redis->getDBNum(), 1); - - $redis = new Swoole\Coroutine\Redis(['database' => 1]); - // connected but not selected - Assert::false($redis->getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 1); - // set database but failed - $redis = new Swoole\Coroutine\Redis(['database' => 1001]); - Assert::false($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getDBNum()); - Assert::same($redis->errCode, SOCKET_EINVAL); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/getOptions.phpt b/tests/swoole_redis_coro/getOptions.phpt deleted file mode 100644 index 7626bd2bc49..00000000000 --- a/tests/swoole_redis_coro/getOptions.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client get options ---SKIPIF-- - ---FILE-- - 100, - 'socket_timeout' => 100, -]); -$redis = new Swoole\Coroutine\Redis(); -var_dump($redis->getOptions()); -$redis->setOptions([ - 'connect_timeout' => 0.001, - 'timeout' => 0.001, - 'serialize' => true, - 'reconnect' => 3 -]); -var_dump($redis->getOptions()); -?> ---EXPECT-- -array(6) { - ["connect_timeout"]=> - float(2) - ["timeout"]=> - float(100) - ["serialize"]=> - bool(false) - ["reconnect"]=> - int(1) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} -array(6) { - ["connect_timeout"]=> - float(0.001) - ["timeout"]=> - float(0.001) - ["serialize"]=> - bool(true) - ["reconnect"]=> - int(3) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} diff --git a/tests/swoole_redis_coro/hgetall.phpt b/tests/swoole_redis_coro/hgetall.phpt deleted file mode 100644 index 7990aa642dc..00000000000 --- a/tests/swoole_redis_coro/hgetall.phpt +++ /dev/null @@ -1,97 +0,0 @@ ---TEST-- -swoole_redis_coro: hGetAll hmGet zRange zRevRange zRangeByScore zRevRangeByScore ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('hkey'); - $redis->hSet('hkey', false, 'val0'); - $redis->hSet('hkey', "field", 'val1'); - $redis->hSet('hkey', 5, 'val5'); - - $redis->delete('zkey'); - $redis->zAdd('zkey', "field", 'val0'); - $redis->zAdd('zkey', true, 'val1'); - $redis->zAdd('zkey', 5, 'val5'); - - echo "-----get---\n"; - var_dump($redis->get('novalue')); - echo "-----zRank---\n"; - var_dump($redis->zRank('novalue', 1)); - echo "-----hGetAll---\n"; - var_dump($redis->hGetAll('hkey')); - echo "-----hmGet---\n"; - var_dump($redis->hmGet('hkey', [3, 5])); - echo "-----zRange---\n"; - var_dump($redis->zRange('zkey', 0, 99, true)); - echo "-----zRevRange---\n"; - var_dump($redis->zRevRange('zkey', 0, 99, true)); - echo "-----zRangeByScore---\n"; - var_dump($redis->zRangeByScore('zkey', 0, 99, ['withscores' => true])); - echo "-----zRevRangeByScore---\n"; - var_dump($redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true])); -}); -?> ---EXPECT-- ------get--- -bool(false) ------zRank--- -bool(false) ------hGetAll--- -array(3) { - [""]=> - string(4) "val0" - ["field"]=> - string(4) "val1" - [5]=> - string(4) "val5" -} ------hmGet--- -array(2) { - [3]=> - bool(false) - [5]=> - string(4) "val5" -} ------zRange--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRange--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} ------zRangeByScore--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRangeByScore--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} diff --git a/tests/swoole_redis_coro/lock.phpt b/tests/swoole_redis_coro/lock.phpt deleted file mode 100644 index 4269a934027..00000000000 --- a/tests/swoole_redis_coro/lock.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis lock ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert($ret); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert(!$ret); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); - usleep(50 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert(!$ret); - usleep(100 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/multi_exec.phpt b/tests/swoole_redis_coro/multi_exec.phpt deleted file mode 100644 index 0a3ed2f8009..00000000000 --- a/tests/swoole_redis_coro/multi_exec.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis multi and exec ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT, false); - Assert::assert($result); - - Assert::assert($redis->hmset('u:i:1', ['a' => 'hello', 'b' => 'world'])); - Assert::assert($redis->hmset('u:i:2', ['a' => 'rango', 'b' => 'swoole'])); - Assert::assert($redis->multi()); - $redis->hmget('u:i:1', array('a', 'b')); - $redis->hmget('u:i:2', array('a', 'b')); - - $rs = $redis->exec(); - Assert::assert($rs and is_array($rs)); - Assert::same($rs[0][0], 'hello'); - Assert::same($rs[0][1], 'world'); - Assert::same($rs[1][0], 'rango'); - Assert::same($rs[1][1], 'swoole'); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/pool.phpt b/tests/swoole_redis_coro/pool.phpt deleted file mode 100644 index 28d93c97679..00000000000 --- a/tests/swoole_redis_coro/pool.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$count = 0; -$pool = new SplQueue(); - -$pm->childFunc = function () use ($pm) -{ - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (\swoole_server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) - { - global $count, $pool; - if (count($pool) == 0) - { - $redis = new Swoole\Coroutine\Redis(); - $redis->id = $count; - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if ($res == false) - { - fail: - $response->end("ERROR\n"); - return; - } - $count++; - $pool->enqueue($redis); - } - - $redis = $pool->dequeue(); - $ret = $redis->set('key', 'value'); - if ($ret) - { - $response->end("OK[$count]\n"); - } - else - { - goto fail; - } - $pool->enqueue($redis); - - }); - - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK[1] -OK[1] -OK[1] diff --git a/tests/swoole_redis_coro/psubscribe_1.phpt b/tests/swoole_redis_coro/psubscribe_1.phpt deleted file mode 100644 index 1945ac083ab..00000000000 --- a/tests/swoole_redis_coro/psubscribe_1.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'test.*'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test.a', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_2.phpt b/tests/swoole_redis_coro/psubscribe_2.phpt deleted file mode 100644 index 255a5eef5d7..00000000000 --- a/tests/swoole_redis_coro/psubscribe_2.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . floor($i / 10) . $i; - $val = $redis->psubscribe([$channel . '*']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::same($val[0], 'psubscribe'); - Assert::same($val[1], $channel . '*'); - - $channel .= 'test'; - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_eof_1.phpt b/tests/swoole_redis_coro/psubscribe_eof_1.phpt deleted file mode 100644 index 8c2b444a0ba..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_1.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 1 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - while ($client = $sock->accept(-1)) { - $client->close(); - } - echo "DONE\n"; -}); - -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::false($val); - Assert::false($redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECONNRESET); - } - } - $redis->close(); - $sock->close(); -}); -swoole_event_wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/psubscribe_eof_2.phpt b/tests/swoole_redis_coro/psubscribe_eof_2.phpt deleted file mode 100644 index 7e7658e1cda..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_2.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 2 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - - while ($client = $sock->accept(-1)) { - $client->recv(); - $client->send("*3\r\n\$10\r\npsubscribe\r\n\$8\r\nchannel1\r\n:1\r\n"); - co::sleep(0.1); - $client->close(); - } - - echo "DONE\n"; -}); -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - - $val = $redis->psubscribe(['channel1']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'channel1'); - - $val = $redis->recv(); - Assert::false($val); - - Assert::false($redis->connected); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - - $redis->close(); - $sock->close(); -}); -swoole_event_wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/punsubscribe.phpt b/tests/swoole_redis_coro/punsubscribe.phpt deleted file mode 100644 index 6326828cadf..00000000000 --- a/tests/swoole_redis_coro/punsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->psubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'psubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/punsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/reconnect.phpt b/tests/swoole_redis_coro/reconnect.phpt deleted file mode 100644 index 613dd17990a..00000000000 --- a/tests/swoole_redis_coro/reconnect.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res); - $redis->close(); - $res2 = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/request_without_connected.phpt b/tests/swoole_redis_coro/request_without_connected.phpt deleted file mode 100644 index f323ff7db2d..00000000000 --- a/tests/swoole_redis_coro/request_without_connected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis request without connected ---SKIPIF-- - ---FILE-- -get('foo')); - echo "DONE\n"; -}); -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::get(): The host is empty in %s/tests/swoole_redis_coro/request_without_connected.php on line 5 -DONE diff --git a/tests/swoole_redis_coro/select.phpt b/tests/swoole_redis_coro/select.phpt deleted file mode 100644 index 770c4f28a22..00000000000 --- a/tests/swoole_redis_coro/select.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->select(0)); - Assert::assert($redis->set('foo', $random0 = get_safe_random())); - Assert::assert($redis->select(1)); - Assert::assert($redis->set('foo', $random1 = get_safe_random())); - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - Assert::assert($redis->select(0)); - $foo = $redis->get('foo'); - Assert::same($foo, $random0); - Assert::assert($foo !== $random1); - Assert::assert($redis->select(1)); - - // test whether it's OK after automatic reconnected - $redis_killer = new Swoole\Coroutine\Redis; - $redis_killer->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis_killer->request(['CLIENT', 'KILL', 'TYPE', 'normal']); - - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/set.phpt b/tests/swoole_redis_coro/set.phpt deleted file mode 100644 index fe2c7174b35..00000000000 --- a/tests/swoole_redis_coro/set.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: set ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($redis->set('key1', 'value')); - Assert::assert($redis->set('key1', 'value', 10)); - Assert::assert($redis->ttl('key1') == 10); - /** - * xx+ex - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'ex' => 30])); - Assert::assert($redis->ttl('key1') == 30); - /** - * delete - */ - Assert::assert($redis->delete('key1')); - /** - * nx+ex - */ - Assert::assert($redis->set('key1', 'value', ['nx', 'ex' => 20])); - Assert::assert($redis->ttl('key1') == 20); - - /** - * px - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'px' => 10000])); - Assert::assert($redis->ttl('key1') == 10); - echo "OK\n"; -}); - -swoole_event::wait(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/setOptions.phpt b/tests/swoole_redis_coro/setOptions.phpt deleted file mode 100644 index 7f14bca2248..00000000000 --- a/tests/swoole_redis_coro/setOptions.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - // read time out - $redis->setOptions(['timeout' => 0.001]); - $s = microtime(true); - $ret = $redis->brpoplpush('test', 'test2', 1); - $s = microtime(true) - $s; - time_approximate(0.001, $s, 1); - Assert::assert(!$ret); - - // read ok (after internal auto connect) - $redis->setOptions(['timeout' => 1]); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - Assert::same($redis->errCode, 0); - Assert::same($redis->errMsg, ''); - $redis->close(); - Assert::assert(!$redis->connected); - - // connect timeout - $redis->setOptions(['connect_timeout' => 0.001]); - $redis->connect('www.google.com', 80); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/stream.phpt b/tests/swoole_redis_coro/stream.phpt deleted file mode 100644 index 00f7d03520b..00000000000 --- a/tests/swoole_redis_coro/stream.phpt +++ /dev/null @@ -1,124 +0,0 @@ ---TEST-- -swoole_redis_coro: stream ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -Co\run(function() { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $ret = $redis->del('mystream'); - - // xGroupCreate - $ret = $redis->xGroupCreate('mystream', 'group1', '0-0', true); - Assert::assert($ret == '1'); - - // xGroupCreateConsumer - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer2'); - Assert::assert($ret == '1'); - - // xAdd - $ret = $redis->xAdd('mystream', '0-1', ['field'=>'111'], ['nomkstream'=>true, 'maxlen'=>['~', 5], 'limit'=>5]); - Assert::assert($ret == '0-1'); - $ret = $redis->xAdd('mystream', '0-2', ['field'=>'222'], ['nomkstream'=>false, 'minid'=>['~', '0-0'], 'limit'=>5]); - Assert::assert($ret == '0-2'); - $ret = $redis->xAdd('mystream', '0-3', ['field'=>'333'], ['maxlen'=>['=', 5]]); - Assert::assert($ret == '0-3'); - $ret = $redis->xAdd('mystream', '0-4', ['field'=>'444'], ['maxlen'=>5]); - Assert::assert($ret, '0-4'); - $ret = $redis->xAdd('mystream', '0-5', ['field'=>'555']); - Assert::assert($ret, '0-5'); - - // xLen - $ret = $redis->xLen('mystream'); - Assert::assert($ret == '5'); - - // xRead - $ret = $redis->xRead(['mystream'=>'0-3'], ['count'=>1, 'block'=>100]); - Assert::assert($ret[0][1][0][0] == '0-4'); - - // xRange - $ret = $redis->xRange('mystream', '0-2', '0-3', 1); - Assert::assert($ret[0][0] == '0-2'); - - // xRevRange - $ret = $redis->xRevRange('mystream', '+', '-', 1); - Assert::assert($ret[0][0] == '0-5'); - - // xReadGroup - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>true]); - Assert::assert($ret[0][1][0][0] == '0-1'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>false]); - Assert::assert($ret[0][1][0][0] == '0-2'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1]); - Assert::assert($ret[0][1][0][0] == '0-3'); - - // xPending - $ret = $redis->xPending('mystream', 'group1', ['start'=>'-', 'end'=>'+', 'count'=>5]); - Assert::assert(count($ret) == 2); - Assert::assert($ret[0][0] == '0-2'); - Assert::assert($ret[1][0] == '0-3'); - - // xAck - $ret = $redis->xAck('mystream', 'group1', ['0-2']); - Assert::assert($ret == '1'); - - // xClaim - $ret = $redis->xClaim('mystream', 'group1', 'consumer2', 0, ['0-3']); - Assert::assert($ret[0][0] == '0-3'); - - // xInfoConsumers - $ret = $redis->xInfoConsumers('mystream', 'group1'); - Assert::assert($ret[1][3] == '1'); - - // xAutoClaim - $ret = $redis->xAutoClaim('mystream', 'group1', 'consumer1', 0, '0-3'); - Assert::assert($ret[1][0][0] == '0-3'); - - // xInfoGroups - $ret = $redis->xInfoGroups('mystream'); - Assert::assert($ret[0][1] == 'group1'); - Assert::assert($ret[0][5] == '1'); - - // xInfoStream - $ret = $redis->xInfoStream('mystream'); - Assert::assert($ret[1] == '5'); - - // xDel - $ret = $redis->xDel('mystream', '0-1', '0-2'); - Assert::assert($ret == '2'); - - // xTrim - $ret = $redis->xTrim('mystream', ['maxlen'=>1]); - Assert::assert($ret == '2'); - $ret = $redis->xTrim('mystream', ['minid'=>['~', '0'], 'limit'=>1]); - Assert::assert($ret == '0'); - - // xGroupSetId - $ret = $redis->xGroupSetId('mystream', 'group1', '0-1'); - Assert::assert($ret == '1'); - - // xGroupDelConsumer - $ret = $redis->xGroupDelConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - - // xGroupDestroy - $ret = $redis->xGroupDestroy('mystream', 'group1'); - Assert::assert($ret == '1'); - - $ret = $redis->del('mystream'); - - echo "OK\n"; -}); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/subscribe_1.phpt b/tests/swoole_redis_coro/subscribe_1.phpt deleted file mode 100644 index e9b4db226c0..00000000000 --- a/tests/swoole_redis_coro/subscribe_1.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 1 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->subscribe(['test']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == 'test'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_2.phpt b/tests/swoole_redis_coro/subscribe_2.phpt deleted file mode 100644 index e60c810baae..00000000000 --- a/tests/swoole_redis_coro/subscribe_2.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . $i; - $val = $redis->subscribe([$channel]); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == $channel); - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - Assert::same($val[1] ?? '', $channel); - Assert::same($val[2] ?? '', 'test' . $channel); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_multi.phpt b/tests/swoole_redis_coro/subscribe_multi.phpt deleted file mode 100644 index ce269995409..00000000000 --- a/tests/swoole_redis_coro/subscribe_multi.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe multi ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $val = $redis->subscribe(['test1', 'test2', 'test3']); - Assert::assert($val); - - for ($i = 0; $i < 3; ++$i) - { - $val = $redis->recv(); - Assert::same($val[0], 'subscribe'); - } - - for ($i = 0; $i < 3; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - $ret = $redis->publish('test1', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test2', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test3', 'hello'); - Assert::assert($ret); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt b/tests/swoole_redis_coro/subscribe_punsubscribe.phpt deleted file mode 100644 index daaba685cb6..00000000000 --- a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt +++ /dev/null @@ -1,54 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe and use punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 22 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 39 diff --git a/tests/swoole_redis_coro/subscribe_reconnect.phpt b/tests/swoole_redis_coro/subscribe_reconnect.phpt deleted file mode 100644 index da9026bbdaf..00000000000 --- a/tests/swoole_redis_coro/subscribe_reconnect.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - $redis->close(); - - $ret = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/timeout.phpt b/tests/swoole_redis_coro/timeout.phpt deleted file mode 100644 index d22fd33e1c6..00000000000 --- a/tests/swoole_redis_coro/timeout.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client timeout ---SKIPIF-- - ---FILE-- - 0.5]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $keyArray = [QUEUE_KEY_1, QUEUE_KEY_2]; - - $s = microtime(true); - $res = $redis->blpop($keyArray, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - // right way: no timeout - $redis->setOptions(['timeout' => -1]); - - $s = microtime(true); - $res = $redis->blpop($keyArray, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); -}); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unconnected.phpt b/tests/swoole_redis_coro/unconnected.phpt deleted file mode 100644 index 83f3d4579f4..00000000000 --- a/tests/swoole_redis_coro/unconnected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unconnected recv ---SKIPIF-- - ---FILE-- -setDefer(true); - Assert::false($redis->recv()); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unixsocket.phpt b/tests/swoole_redis_coro/unixsocket.phpt deleted file mode 100644 index 0eb51155c88..00000000000 --- a/tests/swoole_redis_coro/unixsocket.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 100]); - Assert::assert($redis->connect('unix:/' . REDIS_SERVER_PATH, 0)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -swoole_event_wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unsubscribe.phpt b/tests/swoole_redis_coro/unsubscribe.phpt deleted file mode 100644 index 562351cf304..00000000000 --- a/tests/swoole_redis_coro/unsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/unsubscribe_all.phpt b/tests/swoole_redis_coro/unsubscribe_all.phpt deleted file mode 100644 index 3ffbffeb04a..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[2], 1 - $i); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_all.php on line 24 diff --git a/tests/swoole_redis_coro/unsubscribe_not_all.phpt b/tests/swoole_redis_coro/unsubscribe_not_all.phpt deleted file mode 100644 index 69776291c41..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_not_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe not all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 24 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 41 diff --git a/tests/swoole_redis_coro/zpop.phpt b/tests/swoole_redis_coro/zpop.phpt deleted file mode 100644 index 09025992467..00000000000 --- a/tests/swoole_redis_coro/zpop.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_redis_coro: zPopMin zPopMax bzPopMin bzPopMax ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('zkeyA'); - $redis->zAdd('zkeyA', 1, 'val1'); - $redis->zAdd('zkeyA', 2, 'val2'); - $redis->zAdd('zkeyA', 3, 'val3'); - $redis->zAdd('zkeyA', 4, 'val4'); - $redis->zAdd('zkeyA', 5, 'val5'); - - $redis->delete('zkeyB'); - $redis->zAdd('zkeyB', 1, 'val1'); - $redis->zAdd('zkeyB', 2, 'val2'); - $redis->zAdd('zkeyB', 3, 'val3'); - $redis->zAdd('zkeyB', 4, 'val4'); - $redis->zAdd('zkeyB', 5, 'val5'); - - echo "-----zPopMin---\n"; - var_dump($redis->zPopMin('zkeyA')); - echo "-----zPopMax---\n"; - var_dump($redis->zPopMax('zkeyB')); - echo "-----bzPopMin---\n"; - var_dump($redis->bzPopMin(['zkeyB','zkeyA'], 2)); - echo "-----bzPopMax---\n"; - var_dump($redis->bzPopMax('zkeyB','zkeyA', 2)); - echo "-----bzPopMin no data---\n"; - var_dump($redis->bzPopMin('zkeyC','zkeyD', 2)); -}); -?> ---EXPECT-- ------zPopMin--- -array(2) { - [0]=> - string(4) "val1" - [1]=> - string(1) "1" -} ------zPopMax--- -array(2) { - [0]=> - string(4) "val5" - [1]=> - string(1) "5" -} ------bzPopMin--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val1" - [2]=> - string(1) "1" -} ------bzPopMax--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val4" - [2]=> - string(1) "4" -} ------bzPopMin no data--- -NULL diff --git a/tests/swoole_redis_server/big_packet.phpt b/tests/swoole_redis_server/big_packet.phpt index eab65d2f8db..a0053e111c0 100644 --- a/tests/swoole_redis_server/big_packet.phpt +++ b/tests/swoole_redis_server/big_packet.phpt @@ -5,45 +5,39 @@ swoole_redis_server: test big packet --FILE-- parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $redis = new redis; $redis->connect('127.0.0.1', $pm->getFreePort()); $redis->set('big_value', str_repeat('A', VALUE_LEN)); $ret = $redis->get('big_value'); Assert::same(strlen($ret ?? '' ?: ''), VALUE_LEN); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->data = array(); $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) - { + if (count($data) == 0) { return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); } $key = $data[0]; - if (empty($server->data[$key])) - { + if (empty($server->data[$key])) { $server->send($fd, Server::format(Server::NIL)); - } - else - { + } else { $server->send($fd, Server::format(Server::STRING, $server->data[$key])); } }); $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) - { + if (count($data) < 2) { return Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command"); } $key = $data[0]; diff --git a/tests/swoole_redis_server/format.phpt b/tests/swoole_redis_server/format.phpt index c68e37f4ba5..cc1858815bb 100644 --- a/tests/swoole_redis_server/format.phpt +++ b/tests/swoole_redis_server/format.phpt @@ -14,7 +14,7 @@ echo Server::format(Server::STATUS, "SUCCESS"); echo Server::format(Server::INT, 1000); echo Server::format(Server::STRING, "hello swoole"); echo Server::format(Server::SET, ["php", "is", "best"]); -echo Server::format(Server::MAP, ["php" => 99, "java" => 88, "c++" => '666']); +echo Server::format(Server::MAP, ["php" => '99', "java" => '88', "c++" => '666', 9999 => 'hello']); ?> --EXPECT-- -ERR @@ -32,7 +32,7 @@ $2 is $4 best -*6 +*8 $3 php $2 @@ -45,3 +45,7 @@ $3 c++ $3 666 +$4 +9999 +$5 +hello diff --git a/tests/swoole_redis_server/nested_map.phpt b/tests/swoole_redis_server/nested_map.phpt new file mode 100644 index 00000000000..ffa27ae6873 --- /dev/null +++ b/tests/swoole_redis_server/nested_map.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_redis_server: nested map +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $redis = new Predis\Client('tcp://127.0.0.1:' . $pm->getFreePort()); + $map = $redis->get('map'); + Assert::notEmpty($map); + Assert::eq($map[0], 'uuid'); + Assert::eq($map[1], UUID); + Assert::isArray($map[3]); + Assert::eq($map[5], NUMBER); + Assert::eq($map[3][1], NUMBER); + Assert::eq($map[3][2], UUID); + + $set = $redis->get('set'); + Assert::notEmpty($set); + Assert::eq($set[0], UUID); + Assert::isArray($set[1]); + Assert::eq($set[2], NUMBER); + Assert::eq($set[1][1], NUMBER); + Assert::eq($set[1][3], UUID); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->setHandler('GET', function ($fd, $data) use ($server) { + $key = $data[0]; + if ($key == 'map') { + $out = Server::format(Server::MAP, [ + 'uuid' => UUID, + 'list' => [1, NUMBER, UUID], + 'number' => NUMBER, + ]); + } elseif ($key == 'set') { + $out = Server::format(Server::SET, [ + UUID, + ['number' => NUMBER, 'uuid' => UUID], + NUMBER, + ]); + } else { + $out = Server::format(Server::ERROR, 'bad key'); + } + $server->send($fd, $out); + }); + + $server->on('WorkerStart', function ($server) use ($pm) { + $pm->wakeup(); + }); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_runtime/accept.phpt b/tests/swoole_runtime/accept.phpt index c8f7fe84f85..bb97c5aaa95 100644 --- a/tests/swoole_runtime/accept.phpt +++ b/tests/swoole_runtime/accept.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECT-- diff --git a/tests/swoole_runtime/accept_timeout.phpt b/tests/swoole_runtime/accept_timeout.phpt index bbeaabe35a1..62a970ab9f9 100644 --- a/tests/swoole_runtime/accept_timeout.phpt +++ b/tests/swoole_runtime/accept_timeout.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- diff --git a/tests/swoole_runtime/base.phpt b/tests/swoole_runtime/base.phpt index ef2e31fef0a..9f907e3edda 100644 --- a/tests/swoole_runtime/base.phpt +++ b/tests/swoole_runtime/base.phpt @@ -7,7 +7,7 @@ swoole_runtime: base require __DIR__ . '/../include/bootstrap.php'; $server = SwooleTest\CoServer::createTcpGreeting(); $server->run(); -Swoole\Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP); +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP); go(function () { usleep(1000); echo '1' . PHP_EOL; @@ -42,7 +42,7 @@ go(function () use ($server) { $server->shutdown(); }); echo '5' . PHP_EOL; -Swoole\Runtime::enableCoroutine(true); // all +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // all go(function () { usleep(5 * 1000); echo 'sleep1' . PHP_EOL; @@ -57,7 +57,7 @@ go(function () use ($server) { }); echo '7' . PHP_EOL; Swoole\Event::wait(); -Swoole\Runtime::enableCoroutine(false); // disable all +Swoole\Runtime::enableCoroutine(0); // disable all ?> --EXPECT-- 1 diff --git a/tests/swoole_runtime/block.phpt b/tests/swoole_runtime/block.phpt index 825f185c4bf..9cd9e44f6b9 100644 --- a/tests/swoole_runtime/block.phpt +++ b/tests/swoole_runtime/block.phpt @@ -27,7 +27,7 @@ for ($i = 5; $i--;) { Assert::same($pdo->errorCode(), PDO::ERR_NONE); }); } -swoole_event_wait(); +Swoole\Event::wait(); Assert::assert((microtime(true) - $start) > 5 * 0.1); echo "DONE\n"; ?> diff --git a/tests/swoole_runtime/bug_4657.phpt b/tests/swoole_runtime/bug_4657.phpt new file mode 100644 index 00000000000..9989bb6a9dc --- /dev/null +++ b/tests/swoole_runtime/bug_4657.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_runtime: bug 4657 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_runtime/bug_5104.phpt b/tests/swoole_runtime/bug_5104.phpt new file mode 100644 index 00000000000..191676a14a5 --- /dev/null +++ b/tests/swoole_runtime/bug_5104.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_runtime: Github#5104 https://github.com/swoole/swoole-src/issues/5104 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_runtime/enable_crypto.phpt b/tests/swoole_runtime/enable_crypto.phpt index 9204c16aad6..bdddae6f9c6 100644 --- a/tests/swoole_runtime/enable_crypto.phpt +++ b/tests/swoole_runtime/enable_crypto.phpt @@ -10,7 +10,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt index d776fad8703..1eaa823f2c6 100644 --- a/tests/swoole_runtime/file_hook/bug_4327.phpt +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -15,38 +15,46 @@ require __DIR__.'/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); +const __ROOT_DIR = 'tmp/'; + function createDirectories($protocol = "") { $barrier = Barrier::make(); - $first = "$protocol/".rand(0, 1000); - $second = "/".rand(0, 1000); - $third = "/".rand(0, 1000)."/"; + $first = "$protocol/" . __ROOT_DIR . rand(0, 1000); + $second = "/" . rand(0, 1000); + $third = "/" . rand(0, 1000) . "/"; for ($i = 0; $i < 5; $i++) { Coroutine::create(static function () use ($i, $first, $second, $third, $barrier) { - if (!mkdir($directory = $first.$second.$third.$i, 0755, true) && !is_dir($directory)) { + if (!mkdir($directory = $first . $second . $third . $i, 0755, true) && !is_dir($directory)) { throw new Exception("create directory failed"); } rmdir($directory); }); } - echo "SUCCESS".PHP_EOL; + echo "SUCCESS" . PHP_EOL; Barrier::wait($barrier); - rmdir($first.$second.$third); - rmdir($first.$second); + rmdir($first . $second . $third); + rmdir($first . $second); rmdir($first); } - run(function () { createDirectories(); createDirectories("file://"); }); -Swoole\Runtime::enableCoroutine(false); -createDirectories(); -createDirectories("file://"); +if (defined('SWOOLE_THREAD')) { + echo "SUCCESS" . PHP_EOL; + echo "SUCCESS" . PHP_EOL; +} else { + run(function () { + Swoole\Runtime::enableCoroutine(false); + createDirectories(); + createDirectories("file://"); + }); +} ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_runtime/file_hook/co_fread.phpt b/tests/swoole_runtime/file_hook/co_fread.phpt deleted file mode 100644 index 803d85fc720..00000000000 --- a/tests/swoole_runtime/file_hook/co_fread.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_runtime/file_hook: fread ---SKIPIF-- - ---FILE-- - ---EXPECT-- -open -read diff --git a/tests/swoole_runtime/file_hook/fgets.phpt b/tests/swoole_runtime/file_hook/fgets.phpt new file mode 100644 index 00000000000..9631b564c1f --- /dev/null +++ b/tests/swoole_runtime/file_hook/fgets.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_runtime/file_hook: fgets +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/file_hook/flock.phpt b/tests/swoole_runtime/file_hook/flock.phpt index f17d1012075..94ce4010607 100644 --- a/tests/swoole_runtime/file_hook/flock.phpt +++ b/tests/swoole_runtime/file_hook/flock.phpt @@ -25,7 +25,7 @@ go(function () use ($num) { go('test_flock'); } }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> diff --git a/tests/swoole_runtime/file_hook/fread.phpt b/tests/swoole_runtime/file_hook/fread.phpt new file mode 100644 index 00000000000..a8d45b341c5 --- /dev/null +++ b/tests/swoole_runtime/file_hook/fread.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_runtime/file_hook: fread +--SKIPIF-- + +--FILE-- + +--EXPECT-- +open +read diff --git a/tests/swoole_runtime/file_hook/include.phpt b/tests/swoole_runtime/file_hook/include.phpt index 7fa7acea001..504da522a56 100644 --- a/tests/swoole_runtime/file_hook/include.phpt +++ b/tests/swoole_runtime/file_hook/include.phpt @@ -8,13 +8,13 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECT-- A diff --git a/tests/swoole_runtime/file_hook/lock_ex.phpt b/tests/swoole_runtime/file_hook/lock_ex.phpt index a44e0f3b76e..145bbe79f77 100644 --- a/tests/swoole_runtime/file_hook/lock_ex.phpt +++ b/tests/swoole_runtime/file_hook/lock_ex.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_nb_1.phpt b/tests/swoole_runtime/file_hook/lock_nb_1.phpt index ac2679d1a36..c76e0950969 100644 --- a/tests/swoole_runtime/file_hook/lock_nb_1.phpt +++ b/tests/swoole_runtime/file_hook/lock_nb_1.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_nb_2.phpt b/tests/swoole_runtime/file_hook/lock_nb_2.phpt index a3b5152f099..b93de200c82 100644 --- a/tests/swoole_runtime/file_hook/lock_nb_2.phpt +++ b/tests/swoole_runtime/file_hook/lock_nb_2.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_sh.phpt b/tests/swoole_runtime/file_hook/lock_sh.phpt index 843c852a20e..cf93a4cc9e0 100644 --- a/tests/swoole_runtime/file_hook/lock_sh.phpt +++ b/tests/swoole_runtime/file_hook/lock_sh.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- %d OK diff --git a/tests/swoole_runtime/file_hook/read.phpt b/tests/swoole_runtime/file_hook/read.phpt index 266b046c019..16fb8a7cd2e 100644 --- a/tests/swoole_runtime/file_hook/read.phpt +++ b/tests/swoole_runtime/file_hook/read.phpt @@ -40,7 +40,7 @@ foreach ($files as &$f) $f['hash'] = md5_file($f['file']); } -swoole\runtime::enableCoroutine(); +Swoole\Runtime::enableCoroutine(); foreach ($files as $k => $v) { @@ -50,6 +50,6 @@ foreach ($files as $k => $v) }); } -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_runtime/file_lock/file_lock_1.phpt b/tests/swoole_runtime/file_lock/file_lock_1.phpt index b9ffc9365ce..dbbf982671a 100644 --- a/tests/swoole_runtime/file_lock/file_lock_1.phpt +++ b/tests/swoole_runtime/file_lock/file_lock_1.phpt @@ -27,7 +27,7 @@ go(function () { co::sleep(2); flock($f, LOCK_UN); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/file_lock/lock_nb.phpt b/tests/swoole_runtime/file_lock/lock_nb.phpt index 02cce32e85b..10fb89ea3ba 100644 --- a/tests/swoole_runtime/file_lock/lock_nb.phpt +++ b/tests/swoole_runtime/file_lock/lock_nb.phpt @@ -13,7 +13,7 @@ go(function () { $fp2 = fopen(FILE, 'w+'); Assert::assert(!flock($fp2, LOCK_EX | LOCK_NB)); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/file_lock/lock_sh_1.phpt b/tests/swoole_runtime/file_lock/lock_sh_1.phpt index a2698fb03a4..6cfa595b506 100644 --- a/tests/swoole_runtime/file_lock/lock_sh_1.phpt +++ b/tests/swoole_runtime/file_lock/lock_sh_1.phpt @@ -36,7 +36,7 @@ go(function () use ($startTime) { $ret = flock($f, LOCK_UN); Assert::assert($ret); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/out_of_coroutine.phpt b/tests/swoole_runtime/out_of_coroutine.phpt new file mode 100644 index 00000000000..0895040b0d7 --- /dev/null +++ b/tests/swoole_runtime/out_of_coroutine.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_runtime: out of coroutine +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/pdo.phpt b/tests/swoole_runtime/pdo.phpt index 19f88964556..21be63a5156 100644 --- a/tests/swoole_runtime/pdo.phpt +++ b/tests/swoole_runtime/pdo.phpt @@ -34,7 +34,7 @@ $start = microtime(true); for ($i = MAX_CONCURRENCY_LOW; $i--;) { go('onRequest'); } -swoole_event_wait(); +Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_LOW); Assert::assert((microtime(true) - $start) < .5); //关闭协程,否则会致命错误 diff --git a/tests/swoole_runtime/proc/1.phpt b/tests/swoole_runtime/proc/1.phpt index 9cec169a950..96d6de881f1 100644 --- a/tests/swoole_runtime/proc/1.phpt +++ b/tests/swoole_runtime/proc/1.phpt @@ -29,7 +29,7 @@ go(function() { echo "I didn't segfault!\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- I didn't segfault! diff --git a/tests/swoole_runtime/proc/2.phpt b/tests/swoole_runtime/proc/2.phpt index 20380e020fd..d135b96b59d 100644 --- a/tests/swoole_runtime/proc/2.phpt +++ b/tests/swoole_runtime/proc/2.phpt @@ -35,7 +35,7 @@ go(function() { echo "Done!\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/proc/3.phpt b/tests/swoole_runtime/proc/3.phpt index 96c9b991857..4bc0e8c9dfe 100644 --- a/tests/swoole_runtime/proc/3.phpt +++ b/tests/swoole_runtime/proc/3.phpt @@ -20,7 +20,7 @@ go(function() { var_dump(count($spec)); var_dump($pipes); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --CLEAN-- --CLEAN-- diff --git a/tests/swoole_runtime/proc/5.phpt b/tests/swoole_runtime/proc/5.phpt index e082bac0292..b1ea690e990 100644 --- a/tests/swoole_runtime/proc/5.phpt +++ b/tests/swoole_runtime/proc/5.phpt @@ -35,7 +35,7 @@ go(function() { echo "END\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --CLEAN-- @@ -60,7 +60,7 @@ array(4) { resource(%d) of type (Unknown) } -Warning: proc_open(test): failed to open stream: %s in %s on line %d +Warning: proc_open(test): %s to open stream: %s in %s on line %d array(4) { [3]=> resource(%d) of type (Unknown) diff --git a/tests/swoole_runtime/sento.phpt b/tests/swoole_runtime/sento.phpt index 63a9df18472..fc2d98b2dae 100644 --- a/tests/swoole_runtime/sento.phpt +++ b/tests/swoole_runtime/sento.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/bug46360.phpt b/tests/swoole_runtime/sockets/basic/bug46360.phpt new file mode 100644 index 00000000000..cbc0e2f93f5 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug46360.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_runtime/sockets/basic: Bug 46360 - TCP_NODELAY constant (sock_get_option, sock_set_option) +--SKIPIF-- + + +--CREDITS-- +Florian Anderiasch +fa@php.net +--FILE-- + +--EXPECTF-- +int(%d) diff --git a/tests/swoole_runtime/sockets/basic/bug49341.phpt b/tests/swoole_runtime/sockets/basic/bug49341.phpt new file mode 100644 index 00000000000..4e12d5c2b0c --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug49341.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_runtime/sockets/basic: add SO_REUSEPORT support for socket_set_option() +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/bug63000.phpt b/tests/swoole_runtime/sockets/basic/bug63000.phpt new file mode 100644 index 00000000000..f78059179aa --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug63000.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_runtime/sockets/basic: Multicast on OSX +--SKIPIF-- + + +--FILE-- + '224.0.0.251', + "interface" => 0, +)); +var_dump($so); +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/bug76839.phpt b/tests/swoole_runtime/sockets/basic/bug76839.phpt new file mode 100644 index 00000000000..6f168fa1e6a --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug76839.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_runtime/sockets/basic: socket_recvfrom may return an invalid 'from' address on MacOS +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' diff --git a/tests/swoole_runtime/sockets/basic/ipv4loop.phpt b/tests/swoole_runtime/sockets/basic/ipv4loop.phpt new file mode 100644 index 00000000000..ef1412e662f --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/ipv4loop.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv4 Loopback test +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc b/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc new file mode 100644 index 00000000000..b2a28bfb02a --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc @@ -0,0 +1,7 @@ + + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc b/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc new file mode 100644 index 00000000000..a80437c6624 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc @@ -0,0 +1,9 @@ + + '224.0.0.23', + "interface" => 'lo', +)); +if ($so === false) { + die('skip interface \'lo\' is unavailable.'); +} +if (!defined("MCAST_BLOCK_SOURCE")) { + die('skip source operations are unavailable'); +}?> +--FILE-- + $mcastaddr, + "interface" => $interface, +)); +var_dump($so); + +$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, $port); +var_dump($r); + +$i = 0; +checktimeout($s, 500); +while (($str = socket_read($s, 3000)) !== FALSE) { + $i++; + echo "$i> ", $str, "\n"; + +if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends2, $m = "ignored mcast packet (different interface)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 3) { + echo "blocking source\n"; + $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 4) { + echo "unblocking source\n"; + $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 5) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 6) { + echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 7) { + echo "leaving source group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 8) { +/* echo "rjsg\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so);*/ + break; +} + +} +}); +?> +--EXPECTF-- +creating send socket bound to 127.0.0.1 +bool(true) +creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to 224.0.0.23 +bool(true) +creating receive socket +bool(true) +bool(true) +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(42) +int(12) +3> mcast packet +blocking source +bool(true) +int(31) +int(14) +4> unicast packet +unblocking source +bool(true) +int(27) +5> mcast packet from 127.0.0.1 +leaving group +bool(true) +int(20) +int(14) +6> unicast packet +joining source group +bool(true) +int(27) +7> mcast packet from 127.0.0.1 +leaving source group +bool(true) +int(20) +int(14) +8> unicast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt new file mode 100644 index 00000000000..ac744f5e09e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt @@ -0,0 +1,75 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv4 send options +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +Setting IP_MULTICAST_TTL +bool(true) +int(9) + +Setting IP_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IP_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt new file mode 100644 index 00000000000..be951940328 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt @@ -0,0 +1,89 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv4 send options with unusual values +--SKIPIF-- + + +--FILE-- +getMessage() . \PHP_EOL; + } + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); + + echo "Setting IP_MULTICAST_TTL with \"254\"\n"; + $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, "254"); + var_dump($r); + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); + + echo "Setting IP_MULTICAST_TTL with -1\n"; +//should give error, not be the same as 255 + try { + $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, -1); + var_dump($r); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); +}); +?> +--EXPECT-- +Setting IP_MULTICAST_LOOP with 256 +bool(true) +int(1) +Setting IP_MULTICAST_LOOP with false +bool(true) +int(0) +Setting IP_MULTICAST_TTL with 256 +bool(false) +int(1) +Setting IP_MULTICAST_TTL with "254" +bool(true) +int(254) +Setting IP_MULTICAST_TTL with -1 +bool(false) +int(254) diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt new file mode 100644 index 00000000000..447607b0156 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt @@ -0,0 +1,242 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv6 receive options +--SKIPIF-- + + 'ff01::114', + "interface" => 0, +)); +if ($so === false) { + die('skip unable to join multicast group on any interface.'); +} +$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', $port); +if ($r === false) { + die('skip unable to send multicast packet.'); +} + +if (!defined("MCAST_JOIN_SOURCE_GROUP")) + die('skip source operations are unavailable'); + +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, +)); +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, + "source" => '2001::dead:beef', +)); +if ($so === false) { + die('skip protocol independent multicast API is unavailable.'); +}?> +--FILE-- + $mcastaddr, + "interface" => $interface, + )) or die("err"); + var_dump($so); + + echo "socket_sendto[1]\n"; + $r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + checktimeout($s, 500); + + echo "socket_recvfrom[1]\n"; + $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort); + var_dump($r, $str, $from); + $sblock = $from; + + $r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + + $i = 0; + checktimeout($s, 500); + while (($str = socket_read($s, 3000)) !== false) { + $i++; + echo "$i> ", $str, "\n"; + + if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 3) { + echo "blocking source\n"; + $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 4) { + echo "unblocking source\n"; + $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 5) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 6) { + echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from desired source", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 7) { + echo "leaving source group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 8) { + /*echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so);*/ + break; + } + } +}); +?> +--EXPECTF-- +creating send socket +creating receive socket +bool(true) +bool(true) +socket_sendto[1] +int(14) +socket_recvfrom[1] +int(14) +string(14) "testing packet" +string(%d) "%s" +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(12) +3> mcast packet +blocking source +bool(true) +int(31) +int(14) +4> unicast packet +unblocking source +bool(true) +int(12) +5> mcast packet +leaving group +bool(true) +int(20) +int(14) +6> unicast packet +joining source group +bool(true) +int(32) +7> mcast packet from desired source +leaving source group +bool(true) +int(20) +int(14) +8> unicast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt new file mode 100644 index 00000000000..ba548d012fe --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt @@ -0,0 +1,138 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv6 receive options (limited) +--SKIPIF-- + + 'ff01::114', + "interface" => 0, +)); +if ($so === false) { + die('skip unable to join multicast group on any interface.'); +} +$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000); +if ($r === false) { + die('skip unable to send multicast packet.'); +} +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, +)); +if (defined("MCAST_JOIN_SOURCE_GROUP")) { + $so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, + "source" => '2001::dead:beef', + )); + if ($so !== false) { + die('skip protocol independent multicast API is available.'); + } +} +?> +--FILE-- + $mcastaddr, + "interface" => $interface, + )) or die("err"); + var_dump($so); + + $r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + checktimeout($s, 500); + $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort); + var_dump($r, $str, $from); + $sblock = $from; + + $r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + + $i = 0; + checktimeout($s, 500); + while (($str = socket_read($s, 3000, 500)) !== false) { + $i++; + echo "$i> ", $str, "\n"; + + if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000); + var_dump($r); + } + if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + } + if ($i == 3) { + break; + } + } +}); +?> +--EXPECTF-- +creating send socket +creating receive socket +bool(true) +bool(true) +int(14) +int(14) +string(14) "testing packet" +string(%d) "%s" +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(12) +3> mcast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt new file mode 100644 index 00000000000..2582076d725 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_runtime/sockets/basic: IPv6 send options +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +Setting IPV6_MULTICAST_TTL +bool(true) +int(9) + +Setting IPV6_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IPV6_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt b/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt new file mode 100644 index 00000000000..c3e233a791d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_runtime/sockets/basic: socket_accept() failure +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_bind.phpt b/tests/swoole_runtime/sockets/basic/socket_bind.phpt new file mode 100644 index 00000000000..57d700d980b --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_bind.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_bind - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +int(16) +NULL diff --git a/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt b/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt new file mode 100644 index 00000000000..86e4484ec7b --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_runtime/sockets/basic: void socket_clear_error ([ resource $socket ] ) ; +--CREDITS-- +marcosptf - - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +int(%d) +int(%d) diff --git a/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt b/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt new file mode 100644 index 00000000000..4a5691d5f47 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_connect - test with empty parameters +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- +getMessage() . \PHP_EOL; + } + try { + Assert::false(socket_connect($s_c, '0.0.0.0')); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + $s_w = socket_connect($s_c, '0.0.0.0', $port); + + socket_close($s_c); +}); +?> +--EXPECTF-- +Too few arguments to function swoole_socket_connect(), 1 passed and at least 2 expected + +Warning: Swoole\Coroutine\Socket::connect(): Invalid port argument[0] in %s on line %d diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt new file mode 100644 index 00000000000..ab99680c9ef --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket_create_listen() returns false, when it cannot bind to the port. +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt new file mode 100644 index 00000000000..ea772c5a269 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket binds on 31338 +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(7) "0.0.0.0" +int(31338) +--CREDITS-- +Till Klampaeckel, till@php.net +PHP Testfest Berlin 2009-05-09 diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt new file mode 100644 index 00000000000..9a044229ba3 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_create_listen - test for used socket +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt new file mode 100644 index 00000000000..88cf90558c9 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_runtime/sockets/basic: Test for socket_create_pair() +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_getopt.phpt b/tests/swoole_runtime/sockets/basic/socket_getopt.phpt new file mode 100644 index 00000000000..a8d04f299e6 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getopt.phpt @@ -0,0 +1,82 @@ +--TEST-- +swoole_runtime/sockets/basic: mixed socket_getopt( resource $socket , int $level , int $optname ) ; +--CREDITS-- +marcosptf - + - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +Setting IP_MULTICAST_TTL +bool(true) +int(9) + +Setting IP_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IP_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt new file mode 100644 index 00000000000..45c8f2531a8 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_getpeername - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +bool(true) +bool(false) +NULL +NULL diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt new file mode 100644 index 00000000000..6edd68625e7 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_getpeername_ipv4loop - basic test +--CREDITS-- +Tatjana Andersen tatjana.andersen@redpill-linpro.com +# TestFest 2009 - NorwayUG +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(9) "127.0.0.1" +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt new file mode 100644 index 00000000000..0229be02ad2 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt @@ -0,0 +1,75 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_getpeername_ipv6loop - basic test +--CREDITS-- +Tatjana Andersen tatjana.andersen@redpill-linpro.com +# TestFest 2009 - NorwayUG +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(3) "::1" +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt b/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt new file mode 100644 index 00000000000..65ea85a660a --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_getsockname - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +string(7) "0.0.0.0" +int(%i) diff --git a/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt b/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt new file mode 100644 index 00000000000..37ddb9eafd0 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_runtime/sockets/basic: Test parameter handling in socket_listen(). +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +bool(false) +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_read_params.phpt b/tests/swoole_runtime/sockets/basic/socket_read_params.phpt new file mode 100644 index 00000000000..b43477ea57f --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_read_params.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_read- test with incorrect parameter +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt new file mode 100644 index 00000000000..8e653310222 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_runtime/sockets/basic: Test parameter handling in socket_select(). +--SKIPIF-- + + +--FILE-- +getMessage() . "\n"; +} +}); +?> +--EXPECTF-- +socket_select(): At least one array argument must be passed +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_select.phpt b/tests/swoole_runtime/sockets/basic/socket_select.phpt new file mode 100644 index 00000000000..561a862c859 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/sockets/basic: Test parameter handling in socket_select(). +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +int(0) +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_select_error.phpt b/tests/swoole_runtime/sockets/basic/socket_select_error.phpt new file mode 100644 index 00000000000..b9d5fcfdb72 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select_error.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_runtime/sockets/basic: socket_select() error conditions +--SKIPIF-- + + +--FILE-- +getMessage(), PHP_EOL; +} +}); +?> +--EXPECT-- +socket_select(): Argument #1 ($read) must only have elements of type Socket, string given diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt new file mode 100644 index 00000000000..aec95f69eda --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() via IPv4 UDP +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +Received Ping! from remote address %s and remote port %d diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt new file mode 100644 index 00000000000..73223eec103 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt @@ -0,0 +1,83 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() via IPv6 UDP +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +Received Ping! from remote address %s and remote port %d diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt new file mode 100644 index 00000000000..239a804f5a3 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() through a Unix domain socket +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +bool(false) +bool(false) +Received Ping! +--CREDITS-- +Falko Menge +PHP Testfest Berlin 2009-05-09 diff --git a/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt b/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt new file mode 100644 index 00000000000..38e12d5cf0d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_runtime/sockets/basic: Test socket_set_block return values +--SKIPIF-- + + +--FILE-- +getMessage(), "\n"; + } +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt b/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt new file mode 100644 index 00000000000..1e3d62f4187 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_runtime/sockets/basic: Test socket_set_nonblock return values +--SKIPIF-- + + +--FILE-- +getMessage(), "\n"; + } +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt b/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt new file mode 100644 index 00000000000..78e808f2b0d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_set_block - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt new file mode 100644 index 00000000000..6aa20d1fe88 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_runtime/sockets/basic:SO_BINDTODEVICE +--DESCRIPTION-- +-Bind to loopback 'lo' device (should exist) +-Bind to unexisting device +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +bool(true) + +Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(%d) failed, Error: No such device[%d] in %s on line %d +bool(false) +--CREDITS-- +Damjan Cvetko, foreach.org diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt new file mode 100644 index 00000000000..ad9df747c12 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_runtime/sockets/basic: Test if socket_set_option() returns 'Unable to set socket option' failure for invalid options +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(%d) failed, Error: Permission denied[13] in %s on line %d +--CREDITS-- +Moritz Neuhaeuser, info@xcompile.net +PHP Testfest Berlin 2009-05-10 diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt new file mode 100644 index 00000000000..2ce3690ea9d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_runtime/sockets/basic: socket_set_option() with IPV6_PKTINFO +--SKIPIF-- + + +--FILE-- + '::1', + "ifindex" => 0 + ])); +//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt(). +//See do_ipv6_getsockopt() on the kernel sources +//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS), +//but not worth it +//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO)); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d +bool(false) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt new file mode 100644 index 00000000000..874db4f9039 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_runtime/sockets/basic:SO_RCVTIMEO +--DESCRIPTION-- +-wrong params +-set/get params comparison +--SKIPIF-- + + +--FILE-- +getMessage() . \PHP_EOL; + } + +//set/get comparison + $options = array("sec" => 1, "usec" => 0); + $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $options); + $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_RCVTIMEO); + + var_dump($retval_2); + var_dump($retval_3 === $options); + socket_close($socket); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): no key "sec" passed in optval in %s on line %d +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt new file mode 100644 index 00000000000..1e4f44528e5 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_runtime/sockets/basic:SO_SEOLINGER +--DESCRIPTION-- +-wrong params +-set/get params comparison +-l_linger not given +--SKIPIF-- + + +--FILE-- +getMessage() . \PHP_EOL; + } + + // set/get comparison + $options = array("l_onoff" => 1, "l_linger" => 1); + $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options); + var_dump($retval_2); + + $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_LINGER); + + // l_linger not given + $options_2 = array("l_onoff" => 1); + try { + var_dump(socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options_2)); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + + var_dump($retval_3["l_linger"] === $options["l_linger"]); + // value of l_onoff is not always 1, Darwin returns 128 + var_dump((bool)$retval_3["l_onoff"] === (bool)$options["l_onoff"]); + + socket_close($socket); +}); +?> +--EXPECTF-- + +Warning: Swoole\Coroutine\Socket::setOption(): no key "l_onoff" passed in optval in %s on line %d +bool(true) + +Warning: Swoole\Coroutine\Socket::setOption(): no key "l_linger" passed in optval in %s on line %d +bool(false) +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt new file mode 100644 index 00000000000..2e84bdeeeb0 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_runtime/sockets/basic:SO_SNDTIMEO +--DESCRIPTION-- +-wrong params +-set/get params comparison +--SKIPIF-- + + +--FILE-- +getMessage() . \PHP_EOL; +} + +//set/get comparison +$options = array("sec" => 1, "usec" => 0); +$retval_2 = socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options); +$retval_3 = socket_get_option( $socket, SOL_SOCKET, SO_SNDTIMEO); + +var_dump($retval_2); +var_dump($retval_3 === $options); +socket_close($socket); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): no key "sec" passed in optval %s on line %d +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt b/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt new file mode 100644 index 00000000000..e39498e4252 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_runtime/sockets/basic: Test socket_setopt() basic functionality +--CREDITS-- +Rodrigo Prado de Jesus +User Group: PHPSP #PHPTestFestBrasil +--SKIPIF-- + + +--FILE-- + 1, "usec" => 0); +$retval_1 = socket_setopt( $socket, SOL_SOCKET, SO_SNDTIMEO, $options); +$retval_2 = socket_getopt( $socket, SOL_SOCKET, SO_SNDTIMEO); + +var_dump($retval_1); +var_dump($retval_2 === $options); +socket_close($socket); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt b/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt new file mode 100644 index 00000000000..80992250e1d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_runtime/sockets/basic: bool socket_shutdown ( resource $socket [, int $how = 2 ] ) ; +--CREDITS-- +marcosptf - - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_strerror.phpt b/tests/swoole_runtime/sockets/basic/socket_strerror.phpt new file mode 100644 index 00000000000..4884d36d55c --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_strerror.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_runtime/sockets/basic: ext/sockets - socket_strerror - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(23) "Operation not permitted" diff --git a/tests/swoole_runtime/sockets/basic/unixloop.phpt b/tests/swoole_runtime/sockets/basic/unixloop.phpt new file mode 100644 index 00000000000..e9100fbbc8e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/unixloop.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_runtime/sockets/basic: Unix domain socket Loopback test +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/import.phpt b/tests/swoole_runtime/sockets/import.phpt new file mode 100644 index 00000000000..a689dfe9c0e --- /dev/null +++ b/tests/swoole_runtime/sockets/import.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_runtime/sockets: import +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(12) "test message" +Done diff --git a/tests/swoole_runtime/sockets/socketpair.phpt b/tests/swoole_runtime/sockets/socketpair.phpt index 55d1e86e26a..8bb2ba13265 100644 --- a/tests/swoole_runtime/sockets/socketpair.phpt +++ b/tests/swoole_runtime/sockets/socketpair.phpt @@ -17,8 +17,9 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); const N = 4; run(function () { - $pair = socket_create_pair(AF_UNIX, SOCK_DGRAM, 0); - + $pair = []; + Assert::true(socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $pair)); + go(function () use ($pair) { $n = N; while ($n--) { diff --git a/tests/swoole_runtime/capture_peer_cert.phpt b/tests/swoole_runtime/ssl/capture_peer_cert.phpt similarity index 84% rename from tests/swoole_runtime/capture_peer_cert.phpt rename to tests/swoole_runtime/ssl/capture_peer_cert.phpt index 6149a6f2747..fb7cfb4b7c8 100644 --- a/tests/swoole_runtime/capture_peer_cert.phpt +++ b/tests/swoole_runtime/ssl/capture_peer_cert.phpt @@ -1,14 +1,14 @@ --TEST-- -swoole_runtime: ssl capture_peer_cert +swoole_runtime/ssl: capture_peer_cert --SKIPIF-- --FILE-- +--FILE-- +\n"; + } else { + $http = "GET / HTTP/1.0\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Close\r\n\r\n"; + fwrite($fp, $http); + $content = ''; + while (!feof($fp)) { + $content .= fread($fp, 1024); + } + fclose($fp); + Assert::assert(strpos($content,'map.baidu.com') !== false); + } +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_runtime/ssl/enable_crypto.phpt b/tests/swoole_runtime/ssl/enable_crypto.phpt new file mode 100644 index 00000000000..8b96ebf7b7b --- /dev/null +++ b/tests/swoole_runtime/ssl/enable_crypto.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_runtime/ssl: stream_socket_enable_crypto +--SKIPIF-- + +--FILE-- +\n"; + } else { + $ready->push(true); + $conn = stream_socket_accept($socket); + + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + fclose($conn); + fclose($socket); + echo "OK\n"; + } +}); + +go(function () use ($ready) { + $ready->pop(); + + $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); + if (!$fp) { + echo "$errstr ($errno)
\n"; + } else { + stream_context_set_option($fp, ["ssl" => [ + "local_cert" => SSL_FILE_DIR . '/client.crt', + "local_pk" => SSL_FILE_DIR . '/client.key', + ]]); + // Enable SSL encryption after the connection is established + Assert::assert(stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)); + $data = fread($fp, 8192); + fclose($fp); + Assert::assert(strpos($data, 'local time') !== false); + echo "OK\n"; + } +}); + +Swoole\Event::wait(); +?> +--EXPECT-- +OK +OK diff --git a/tests/swoole_runtime/ssl/local_cert.phpt b/tests/swoole_runtime/ssl/local_cert.phpt index 0c9152a00c7..c5035337b4d 100644 --- a/tests/swoole_runtime/ssl/local_cert.phpt +++ b/tests/swoole_runtime/ssl/local_cert.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECT-- OK diff --git a/tests/swoole_runtime/ssl/server.phpt b/tests/swoole_runtime/ssl/server.phpt new file mode 100644 index 00000000000..02f4c073b76 --- /dev/null +++ b/tests/swoole_runtime/ssl/server.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_runtime: ssl server +--SKIPIF-- + +--FILE-- +\n"; + } else { + $ready->push(true); + $conn = stream_socket_accept($socket); + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + fclose($conn); + fclose($socket); + echo "OK\n"; + } +}); + +go(function () use ($ready) { + $ready->pop(); + $fp = stream_socket_client("ssl://127.0.0.1:8000", $errno, $errstr, 30); + if (!$fp) { + echo "$errstr ($errno)
\n"; + } else { + $data = fread($fp, 8192); + fclose($fp); + Assert::assert(strpos($data,'local time') !== false); + echo "OK\n"; + } +}); + +Swoole\Event::wait(); +?> +--EXPECT-- +OK +OK diff --git a/tests/swoole_runtime/ssl/without_key.phpt b/tests/swoole_runtime/ssl/without_key.phpt index a4a4efad647..f882e7e2e1d 100644 --- a/tests/swoole_runtime/ssl/without_key.phpt +++ b/tests/swoole_runtime/ssl/without_key.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECTF-- Warning: stream_socket_client(): ssl require key file in %s on line %d diff --git a/tests/swoole_runtime/ssl_client.phpt b/tests/swoole_runtime/ssl_client.phpt deleted file mode 100644 index b465f8ecd1e..00000000000 --- a/tests/swoole_runtime/ssl_client.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -swoole_runtime: ssl client ---SKIPIF-- - ---FILE-- -\n"; - } else { - $http = "GET / HTTP/1.0\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Close\r\n\r\n"; - fwrite($fp, $http); - $content = ''; - while (!feof($fp)) { - $content .= fread($fp, 1024); - } - fclose($fp); - Assert::assert(strpos($content,'map.baidu.com') !== false); - } -}); -swoole_event_wait(); -?> ---EXPECT-- diff --git a/tests/swoole_runtime/ssl_server.phpt b/tests/swoole_runtime/ssl_server.phpt deleted file mode 100644 index 741436fcd5d..00000000000 --- a/tests/swoole_runtime/ssl_server.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -swoole_runtime: ssl server ---SKIPIF-- - ---FILE-- -\n"; - } else { - $ready->push(true); - $conn = stream_socket_accept($socket); - fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); - fclose($conn); - fclose($socket); - echo "OK\n"; - } -}); - -go(function () use ($ready) { - $ready->pop(); - $fp = stream_socket_client("ssl://127.0.0.1:8000", $errno, $errstr, 30); - if (!$fp) { - echo "$errstr ($errno)
\n"; - } else { - $data = fread($fp, 8192); - fclose($fp); - Assert::assert(strpos($data,'local time') !== false); - echo "OK\n"; - } -}); - -swoole_event_wait(); -?> ---EXPECT-- -OK -OK diff --git a/tests/swoole_runtime/stream_context.phpt b/tests/swoole_runtime/stream_context.phpt index a309d26b13e..ff9763c202d 100644 --- a/tests/swoole_runtime/stream_context.phpt +++ b/tests/swoole_runtime/stream_context.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/stream_context_pass_null.phpt b/tests/swoole_runtime/stream_context_pass_null.phpt index be9280f1211..ee6f4359197 100644 --- a/tests/swoole_runtime/stream_context_pass_null.phpt +++ b/tests/swoole_runtime/stream_context_pass_null.phpt @@ -6,13 +6,13 @@ swoole_runtime: stream context pass null --EXPECT-- diff --git a/tests/swoole_runtime/stream_copy_to_stream_socket.phpt b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt new file mode 100644 index 00000000000..5d9deba1687 --- /dev/null +++ b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_runtime: stream_copy_to_stream() with socket as $source +--SKIPIF-- + + +--FILE-- +\n"; + } else { + $local = stream_socket_accept($socket); + $remote = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30, STREAM_CLIENT_CONNECT); + go(function () use ($local, $remote) { + stream_copy_to_stream($local, $remote); + }); + stream_copy_to_stream($remote, $local); + fclose($local); + fclose($remote); + fclose($socket); + } + }); + $fp = stream_socket_client($uri, $errno, $errstr, 30); + if (!$fp) { + echo "$errstr ($errno)
\n"; + } else { + $http = "GET / HTTP/1.0\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Close\r\n\r\n"; + fwrite($fp, $http); + $content = ''; + while (!feof($fp)) { + $content .= fread($fp, 1024); + } + fclose($fp); + Assert::assert(strpos($content,'map.baidu.com') !== false); + } + echo "DONE\n"; +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_runtime/stream_get_meta_data.phpt b/tests/swoole_runtime/stream_get_meta_data.phpt index 7690f6934d1..4d4a5c1fa81 100644 --- a/tests/swoole_runtime/stream_get_meta_data.phpt +++ b/tests/swoole_runtime/stream_get_meta_data.phpt @@ -45,10 +45,10 @@ $pm->childFunc = function () use ($pm) { "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { //donot send any }); $serv->start(); diff --git a/tests/swoole_runtime/stream_select/base.phpt b/tests/swoole_runtime/stream_select/base.phpt index 7870b3b26a1..731f7813d86 100644 --- a/tests/swoole_runtime/stream_select/base.phpt +++ b/tests/swoole_runtime/stream_select/base.phpt @@ -7,9 +7,8 @@ swoole_runtime/stream_select: base require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); go(function () { - Swoole\Runtime::enableCoroutine(); - $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); - $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); + $fp1 = stream_socket_client("tcp://" . TEST_DOMAIN_1 . ":80", $errno, $errstr, 30); + $fp2 = stream_socket_client("tcp://" . TEST_DOMAIN_2 . ":80", $errno, $errstr, 30); if (!$fp1) { echo "$errstr ($errno)
\n"; } else { @@ -28,5 +27,6 @@ go(function () { fclose($fp1); } }); +Swoole\Runtime::enableCoroutine(0); ?> --EXPECT-- diff --git a/tests/swoole_runtime/stream_select/blocked.phpt b/tests/swoole_runtime/stream_select/blocked.phpt index a684299be8d..61bd607763b 100644 --- a/tests/swoole_runtime/stream_select/blocked.phpt +++ b/tests/swoole_runtime/stream_select/blocked.phpt @@ -6,12 +6,12 @@ swoole_runtime/stream_select: blocked \n"; } else { - fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\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"); $r_array = [$fp1, $fp2]; $w_array = $e_array = null; $n = stream_select($r_array, $w_array, $e_array, 10); diff --git a/tests/swoole_runtime/stream_select/bug60602.phpt b/tests/swoole_runtime/stream_select/bug60602.phpt index 6c755e169a4..b173363f3f4 100644 --- a/tests/swoole_runtime/stream_select/bug60602.phpt +++ b/tests/swoole_runtime/stream_select/bug60602.phpt @@ -57,7 +57,7 @@ go(function () { Swoole\Event::wait(); ?> --EXPECTF-- -Notice: Array to string conversion in %s on line %d +%s: Array to string conversion in %s on line %d int(%d) int(0) bool(true) diff --git a/tests/swoole_runtime/stream_select/rw_events.phpt b/tests/swoole_runtime/stream_select/rw_events.phpt index 3de73db53ef..7a7acf604a8 100644 --- a/tests/swoole_runtime/stream_select/rw_events.phpt +++ b/tests/swoole_runtime/stream_select/rw_events.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); -$n = new swoole_atomic(1); +$n = new Swoole\Atomic(1); go(function () use ($n) { $server = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); diff --git a/tests/swoole_runtime/stream_socket_pair.phpt b/tests/swoole_runtime/stream_socket_pair.phpt index b81cb820a7a..bfafb2d63a2 100644 --- a/tests/swoole_runtime/stream_socket_pair.phpt +++ b/tests/swoole_runtime/stream_socket_pair.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- Write byte: 13 diff --git a/tests/swoole_runtime/tcp.phpt b/tests/swoole_runtime/tcp.phpt index 6c997676773..a5da8110865 100644 --- a/tests/swoole_runtime/tcp.phpt +++ b/tests/swoole_runtime/tcp.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/udg.phpt b/tests/swoole_runtime/udg.phpt index 4dc6a955f99..1e91db84207 100644 --- a/tests/swoole_runtime/udg.phpt +++ b/tests/swoole_runtime/udg.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- [Server] recv : hello-0 diff --git a/tests/swoole_runtime/udp.phpt b/tests/swoole_runtime/udp.phpt index bad2ab84ff4..5eb75e470bf 100644 --- a/tests/swoole_runtime/udp.phpt +++ b/tests/swoole_runtime/udp.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECTF-- [Server] recvfrom[127.0.0.1:%d] : hello-0 diff --git a/tests/swoole_runtime/unix.phpt b/tests/swoole_runtime/unix.phpt index 207592399e8..eed84dbaa9b 100644 --- a/tests/swoole_runtime/unix.phpt +++ b/tests/swoole_runtime/unix.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- [Client] recvfrom[test.sock] : Swoole: hello-0 diff --git a/tests/swoole_server/addProcess.phpt b/tests/swoole_server/addProcess.phpt index 0f90c72d8b9..ee2b07acfed 100644 --- a/tests/swoole_server/addProcess.phpt +++ b/tests/swoole_server/addProcess.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $process = new \Swoole\Process(function ($process) use ($serv) { while (1) { $msg = json_decode($process->read(), true); diff --git a/tests/swoole_server/addProcess_with_event_wait.phpt b/tests/swoole_server/addProcess_with_event_wait.phpt index 18dd22cdb4b..93b84742a48 100644 --- a/tests/swoole_server/addProcess_with_event_wait.phpt +++ b/tests/swoole_server/addProcess_with_event_wait.phpt @@ -21,10 +21,10 @@ $pm->childFunc = function () use ($pm) { public function run() { - swoole_timer_tick(100, function (int $id) use (&$i) { + Swoole\Timer::tick(100, function (int $id) use (&$i) { global $pm; if (++$i === 10) { - swoole_timer_clear($id); + Swoole\Timer::clear($id); $pm->wakeup(); } echo "Tick {$i}\n"; diff --git a/tests/swoole_server/addProcess_with_tick.phpt b/tests/swoole_server/addProcess_with_tick.phpt index b05ce2db894..c18e7c89486 100644 --- a/tests/swoole_server/addProcess_with_tick.phpt +++ b/tests/swoole_server/addProcess_with_tick.phpt @@ -18,7 +18,7 @@ class Process3 extends Swoole\Process public function run() { - swoole_timer_tick(100, function () { + Swoole\Timer::tick(100, function () { global $atomic; if ($atomic->add() > 5) { global $pm; diff --git a/tests/swoole_server/base/reload_1.phpt b/tests/swoole_server/base/reload_1.phpt new file mode 100644 index 00000000000..af6f3edbd00 --- /dev/null +++ b/tests/swoole_server/base/reload_1.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_server/base: reload all workers +--SKIPIF-- + +--FILE-- + new Swoole\Atomic(), + 'task_worker' => new Swoole\Atomic() +]; +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + while (!file_exists(TEST_PID_FILE)) { + usleep(100 * 1000); + } + $pid = file_get_contents(TEST_PID_FILE); + $random = mt_rand(1, 5); + usleep(100 * 1000); + for ($n = $random; $n--;) { + Swoole\Process::kill($pid, SIGUSR1); + usleep(200 * 1000); + } + + /**@var $counter Swoole\Atomic[] */ + $total = $counter['worker']->get() - $worker_num; + $expect = $random * $worker_num; + Assert::same($total, $expect, "[worker reload {$total} but expect {$expect}]"); + + $total = $counter['task_worker']->get() - $task_worker_num; + $expect = $random * $task_worker_num; + Assert::same($total, $expect, "[task worker reload {$total} but expect {$random}]"); + + $log = file_get_contents(TEST_LOG_FILE); + $log = trim(preg_replace('/.+?\s+?INFO\s+?.+/', '', $log)); + if (!Assert::assert(empty($log))) { + var_dump($log); + } + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + @unlink(TEST_LOG_FILE); + @unlink(TEST_PID_FILE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'log_file' => TEST_LOG_FILE, + 'pid_file' => TEST_PID_FILE, + 'worker_num' => $worker_num, + 'task_worker_num' => $task_worker_num, + ]); + $server->on('ManagerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { + /**@var $counter Swoole\Atomic[] */ + global $counter; + $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker']; + $atomic->add(1); + }); + $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + }); + $server->on('Task', function () { + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_server/base/reload_2.phpt b/tests/swoole_server/base/reload_2.phpt new file mode 100644 index 00000000000..a94161c4f91 --- /dev/null +++ b/tests/swoole_server/base/reload_2.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_server/base: reload task workers +--SKIPIF-- + +--FILE-- + new Swoole\Atomic(), + 'task_worker' => new Swoole\Atomic() +]; +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + while (!file_exists(TEST_PID_FILE)) { + usleep(100 * 1000); + } + $pid = file_get_contents(TEST_PID_FILE); + $random = mt_rand(1, 5); + usleep(100 * 1000); + for ($n = $random; $n--;) { + Swoole\Process::kill($pid, SIGUSR2); + usleep(200 * 1000); + } + + /**@var $counter Swoole\Atomic[] */ + $total = $counter['worker']->get() - $worker_num; + Assert::same($total, 0, "[worker reload {$total} but expect 0]"); + + $total = $counter['task_worker']->get() - $task_worker_num; + $expect = $random * $task_worker_num; + Assert::same($total, $expect, "[task worker reload {$total} but expect {$expect}]"); + + $log = file_get_contents(TEST_LOG_FILE); + $log = trim(preg_replace('/.+?\s+?INFO\s+?.+/', '', $log)); + if (!Assert::assert(empty($log))) { + var_dump($log); + } + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + @unlink(TEST_LOG_FILE); + @unlink(TEST_PID_FILE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'log_file' => TEST_LOG_FILE, + 'pid_file' => TEST_PID_FILE, + 'worker_num' => $worker_num, + 'task_worker_num' => $task_worker_num, + ]); + $server->on('ManagerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { + /**@var $counter Swoole\Atomic[] */ + global $counter; + $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker']; + $atomic->add(1); + }); + $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + }); + $server->on('Task', function () { + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_server/base/shutdown.phpt b/tests/swoole_server/base/shutdown.phpt new file mode 100644 index 00000000000..12f75f1cbb5 --- /dev/null +++ b/tests/swoole_server/base/shutdown.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_server/base: shutdown +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Co\Client(SWOOLE_SOCK_TCP); + Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); + Assert::assert($client->send($pm->getRandomData()) > 0); + }); + $pm->kill(); + $pm->wait(); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'worker_num' => mt_rand(2, 4), + 'log_file' => '/dev/null', + ]); + $server->on('start', function () use ($pm) { + echo "START\n"; + $pm->wakeup(); + }); + $server->on('receive', function (Swoole\Server $server, int $fd, int $rid, string $data) use ($pm) { + Assert::same($data, $pm->getRandomData()); + $server->shutdown(); + }); + $server->on('shutdown', function () use ($pm) { + echo "SHUTDOWN\n"; + $pm->wakeup(); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +$pm->expectExitCode(0); +?> +--EXPECT-- +START +SHUTDOWN diff --git a/tests/swoole_server/base/shutdown_single.phpt b/tests/swoole_server/base/shutdown_single.phpt new file mode 100644 index 00000000000..f6b39ec42a3 --- /dev/null +++ b/tests/swoole_server/base/shutdown_single.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_server/base: shutdown [single process] +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + go(function () use ($pm) { + $client = new Co\Client(SWOOLE_SOCK_TCP); + Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); + Assert::assert($client->send($pm->getRandomData()) > 0); + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); + $server->on('start', function (Server $server) use ($pm) { + echo "START\n"; + Assert::eq($server->getManagerPid(), 0); + Assert::eq($server->getMasterPid(), posix_getpid()); + $pm->wakeup(); + }); + $server->on('receive', function (Server $server, int $fd, int $rid, string $data) use ($pm) { + Assert::same($data, $pm->getRandomData()); + $server->shutdown(); + }); + $server->on('shutdown', function () { + echo "SHUTDOWN\n"; + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +$pm->expectExitCode(0); +?> +--EXPECT-- +START +SHUTDOWN diff --git a/tests/swoole_server/bigPipeMessage.phpt b/tests/swoole_server/bigPipeMessage.phpt index a5df5e32b8b..54998ea4da9 100644 --- a/tests/swoole_server/bigPipeMessage.phpt +++ b/tests/swoole_server/bigPipeMessage.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm) $pm->childFunc = function () use ($pm, $port) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 2, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/big_session_id.phpt b/tests/swoole_server/big_session_id.phpt index 93995f3adb6..bf69f681a8d 100644 --- a/tests/swoole_server/big_session_id.phpt +++ b/tests/swoole_server/big_session_id.phpt @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm, $port) { global $count; $count++; if ($count == N) { - $serv->defer(function () use ($serv) { + Swoole\Event::defer(function () use ($serv) { foreach ($serv->connections as $fd) { $serv->send($fd, "$fd-OK"); } diff --git a/tests/swoole_server/big_udp_packet.phpt b/tests/swoole_server/big_udp_packet.phpt index f1b5b2e5bcf..e9e04bfc4c8 100644 --- a/tests/swoole_server/big_udp_packet.phpt +++ b/tests/swoole_server/big_udp_packet.phpt @@ -12,29 +12,24 @@ $port = get_one_free_port(); $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ - $client = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); - if (!$client->connect('127.0.0.1', $port)) - { +$pm->parentFunc = function ($pid) use ($port) { + $client = new Swoole\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); } - $client->send(str_repeat('A', N)); + $client->send(str_repeat('A', N)); $data = $client->recv(); Assert::same(strlen($data), N); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP); +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $serv->on("workerStart", function ($serv) use ($pm) - { + $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('packet', function ($serv, $data, $client) - { + $serv->on('packet', function ($serv, $data, $client) { $serv->sendto($client['address'], $client['port'], str_repeat('B', strlen($data))); }); $serv->start(); diff --git a/tests/swoole_server/bug_11000_01.phpt b/tests/swoole_server/bug_11000_01.phpt index a1b489826dd..fbb59ec447f 100644 --- a/tests/swoole_server/bug_11000_01.phpt +++ b/tests/swoole_server/bug_11000_01.phpt @@ -5,19 +5,22 @@ swoole_server: bug_11000_01 --FILE-- childFunc = function () { $port = get_one_free_port(); - $serv = new Server(TCP_SERVER_HOST, $port); - $process = new \Swoole\Process(function ($process) use ($serv) { + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); + $process = new Process(function ($process) use ($serv) { usleep(10000); $stats = $serv->stats(); Assert::isArray($stats); Assert::keyExists($stats, 'connection_num'); Assert::keyExists($stats, 'request_count'); + usleep(200000); $serv->shutdown(); }); $serv->set(['worker_num' => 2, 'log_file' => '/dev/null']); diff --git a/tests/swoole_server/bug_1864.phpt b/tests/swoole_server/bug_1864.phpt index 1a6c847f74f..49e7f5d9f26 100644 --- a/tests/swoole_server/bug_1864.phpt +++ b/tests/swoole_server/bug_1864.phpt @@ -12,8 +12,7 @@ const M = 512; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { function run() { global $pm; @@ -35,14 +34,14 @@ $pm->parentFunc = function ($pid) use ($pm) } } } - for ($i = 0; $i < N; $i ++) { + + for ($i = 0; $i < N; $i++) { run(); } $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $ss = [ 'daemonize' => 0, 'dispatch_mode' => 1, @@ -60,20 +59,18 @@ $pm->childFunc = function () use ($pm) 'log_file' => TEST_LOG_FILE, ]; - $status = new swoole_atomic(0); + $status = new Swoole\Atomic(0); $tcp = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $tcp->set($ss); $tcp->on('receive', function (Server $server, $fd, $reactorID, $data) use ($status) { $size = unpack('N', substr($data, 4, 4))[1]; - if ($size !== strlen($data) - 8) - { + if ($size !== strlen($data) - 8) { $server->shutdown(); $status->set(1); } }); - $tcp->on("WorkerStart", function (Server $serv) use ($pm) - { + $tcp->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); $tcp->on("shutdown", function (Server $serv) use ($status) { diff --git a/tests/swoole_server/bug_2308.phpt b/tests/swoole_server/bug_2308.phpt index c40abe3c2f2..253bb68defb 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -19,10 +19,11 @@ $pm->childFunc = function () use ($pm) { 'worker_num' => MAX_PROCESS_NUM, 'log_file' => '/dev/null', 'enable_coroutine' => false, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('start', function () { - \Swoole\Coroutine::create(function () { - $redis = new \Swoole\Coroutine\Redis(); + Swoole\Coroutine::create(function () { + $redis = new \Redis(); $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); $ret = $redis->set('foo', 'bar'); Assert::assert($ret); @@ -31,7 +32,7 @@ $pm->childFunc = function () use ($pm) { }); }); $server->on('workerStart', function ($server) { - $server->after(1000, function () use ($server) { + Swoole\Timer::after(1000, function () use ($server) { $server->stop(); }); }); diff --git a/tests/swoole_server/bug_2313.phpt b/tests/swoole_server/bug_2313.phpt index fff1eff1f24..3811674f9ce 100644 --- a/tests/swoole_server/bug_2313.phpt +++ b/tests/swoole_server/bug_2313.phpt @@ -7,7 +7,7 @@ swoole_server: bug Github#2313 require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', 9501); + $server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_PROCESS); $process = new Swoole\Process(function () { }); $server->addProcess($process); var_dump($process->id); diff --git a/tests/swoole_server/bug_2639.phpt b/tests/swoole_server/bug_2639.phpt index 68aa90d477c..9d74aa7ab80 100644 --- a/tests/swoole_server/bug_2639.phpt +++ b/tests/swoole_server/bug_2639.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => dirname(__DIR__) . '/include/api/ssl-ca/server-cert.pem', diff --git a/tests/swoole_server/bug_aio.phpt b/tests/swoole_server/bug_aio.phpt index e14e59cce1c..7d16e24a8dc 100644 --- a/tests/swoole_server/bug_aio.phpt +++ b/tests/swoole_server/bug_aio.phpt @@ -8,22 +8,25 @@ require __DIR__ . '/../include/bootstrap.php'; define('__FILE_CONTENTS__', file_get_contents(__FILE__)); $pm = new SwooleTest\ProcessManager; $pm->setWaitTimeout(0); -$pm->parentFunc = function () { }; +$pm->parentFunc = function () { +}; $pm->childFunc = function () use ($pm) { go(function () { Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); // will be discarded }); - swoole_event::wait(); - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); - $server->set(['worker_num' => 1]); + Swoole\Event::wait(); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { echo 'read file' . PHP_EOL; Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); echo 'read file ok' . PHP_EOL; $pm->wakeup(); + usleep(100000); $server->shutdown(); }); - $server->on('Receive', function () { }); + $server->on('Receive', function () { + }); $server->start(); }; $pm->childFirst(); @@ -32,4 +35,3 @@ $pm->run(); --EXPECTF-- read file read file ok -[%s] INFO Server is shutdown now diff --git a/tests/swoole_server/close_in_manager.phpt b/tests/swoole_server/close_in_manager.phpt new file mode 100644 index 00000000000..0d21ffc27ce --- /dev/null +++ b/tests/swoole_server/close_in_manager.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_server: close in manager process +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + swoole_loop_n(N, function () use ($pm) { + go(function () use ($pm) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $client->send("close"); + $data = $client->recv(); + Assert::eq($data, ""); + }); + }); + }); + echo "ALL DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 2, + 'log_file' => TEST_LOG_FILE, + ]); + $serv->on(Constant::EVENT_MANAGER_START, function ($serv) use ($pm) { + Timer::after(200, function () use ($serv) { + foreach ($serv->connections as $fd) { + $serv->close($fd); + } + }); + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $reactor_id, $data) { + }); + $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) { + Assert::assert(posix_getpid() != $serv->manager_pid); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +ALL DONE diff --git a/tests/swoole_server/close_max_fd.phpt b/tests/swoole_server/close_max_fd.phpt index b7be4b40547..eb8dda26947 100644 --- a/tests/swoole_server/close_max_fd.phpt +++ b/tests/swoole_server/close_max_fd.phpt @@ -32,7 +32,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_level' => SWOOLE_LOG_ERROR, diff --git a/tests/swoole_server/connections.phpt b/tests/swoole_server/connections.phpt index 25ac4333ad1..1efca265284 100644 --- a/tests/swoole_server/connections.phpt +++ b/tests/swoole_server/connections.phpt @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm, $port) { global $count; $count++; if ($count == N) { - $serv->defer(function () use ($serv) { + Swoole\Event::defer(function () use ($serv) { foreach ($serv->connections as $fd) { $serv->send($fd, "OK"); } diff --git a/tests/swoole_server/dispatch_mode_1.phpt b/tests/swoole_server/dispatch_mode_1.phpt index 1c0daf64b29..1abc7d4b18f 100644 --- a/tests/swoole_server/dispatch_mode_1.phpt +++ b/tests/swoole_server/dispatch_mode_1.phpt @@ -11,8 +11,10 @@ use Swoole\Coroutine\Client; use Swoole\Timer; use Swoole\Event; use Swoole\Server; +use Swoole\Atomic; global $stats; +$barrier = new Atomic(0); $stats = array(); $count = 0; $port = get_one_free_port(); @@ -58,7 +60,7 @@ $pm->parentFunc = function ($pid) use ($port) { echo "DONE\n"; }; -$pm->childFunc = function () use ($pm, $port) { +$pm->childFunc = function () use ($pm, $port, $barrier) { $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => WORKER_N, @@ -67,8 +69,10 @@ $pm->childFunc = function () use ($pm, $port) { 'open_eof_split' => true, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) { - $pm->wakeup(); + $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) { + if ($barrier->add() == WORKER_N) { + $pm->wakeup(); + } }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) { $serv->send($fd, $serv->worker_id . "\r\n\r\n"); diff --git a/tests/swoole_server/dispatch_mode_10.phpt b/tests/swoole_server/dispatch_mode_10.phpt new file mode 100644 index 00000000000..7f2940af730 --- /dev/null +++ b/tests/swoole_server/dispatch_mode_10.phpt @@ -0,0 +1,94 @@ +--TEST-- +swoole_server: dispatch_mode = 10 [concurrent lb] +--SKIPIF-- + +--FILE-- +column('count', Table::TYPE_INT); +$table->create(); + +const N = 1024; +const EOF = "\r\n\r\n"; + +$pm = new SwooleTest\ProcessManager; +$pm->magic_code = rand(10000000, 90000000); +$pm->parentFunc = function ($pid) use ($pm, $table) { + run(function () use ($pm, $table) { + $rand = rand(1, 4); + $data = array( + 'name' => __FILE__, + 'sid' => $pm->magic_code, + 'content' => str_repeat('A', 1024 * $rand), + ); + $_serialize_data = serialize($data) . EOF; + $n = N; + while ($n--) { + for ($i = 0; $i < 16; $i++) { + go(function () use ($pm, $_serialize_data) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->post('/', $_serialize_data)); + Assert::eq($client->getStatusCode(), 200); + Assert::eq($client->getBody(), "SUCCESS" . EOF); + }); + } + System::sleep(0.002); + } + }); + + $pm->kill(); + + $array = array_column(iterator_to_array($table), 'count'); + $standard_deviation = sqrt(swoole_get_variance(swoole_get_average($array), $array)); + Assert::greaterThan($standard_deviation, 1); + Assert::lessThan($standard_deviation, 5); + echo 'DONE' . PHP_EOL; +}; + +$pm->childFunc = function () use ($pm, $table) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + + $serv->set(array( + 'dispatch_mode' => SWOOLE_DISPATCH_CONCURRENT_LB, + 'worker_num' => 4, + 'log_file' => '/dev/null', + )); + + $serv->on(Constant::EVENT_WORKER_START, function (Server $serv, $worker_id) use ($pm) { + if ($worker_id == 0) { + $pm->wakeup(); + } + }); + + $serv->on(Constant::EVENT_REQUEST, function ($req, $resp) use ($pm, $table, $serv) { + $table->incr($serv->getWorkerId(), 'count'); + if (rand(1000, 9999) % 10 == 0) { + System::sleep(0.5); + } + $_data = unserialize(rtrim($req->getContent())); + if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) { + $resp->end("SUCCESS" . EOF); + } else { + $resp->end("ERROR" . EOF); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/dispatch_mode_3.phpt b/tests/swoole_server/dispatch_mode_3.phpt index 347821a912a..ebb8b801f94 100644 --- a/tests/swoole_server/dispatch_mode_3.phpt +++ b/tests/swoole_server/dispatch_mode_3.phpt @@ -14,8 +14,10 @@ use Swoole\Coroutine\Client; use Swoole\Timer; use Swoole\Event; use Swoole\Server; +use Swoole\Atomic; global $stats; +$barrier = new Atomic(0); $stats = array(); $count = 0; $port = get_one_free_port(); @@ -62,18 +64,20 @@ $pm->parentFunc = function ($pid) use ($port) { echo "DONE\n"; }; -$pm->childFunc = function () use ($pm, $port) { +$pm->childFunc = function () use ($pm, $port, $barrier) { $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( - "worker_num" => WORKER_N, + 'worker_num' => WORKER_N, 'dispatch_mode' => 3, 'package_eof' => "\r\n\r\n", 'enable_coroutine' => false, 'open_eof_split' => true, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) { - $pm->wakeup(); + $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) { + if ($barrier->add() == WORKER_N) { + $pm->wakeup(); + } }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) { if ($serv->worker_id == 10 or $serv->worker_id == 5) { diff --git a/tests/swoole_server/dispatch_mode_7.phpt b/tests/swoole_server/dispatch_mode_7.phpt index e43877bf9fb..70c8c36b721 100644 --- a/tests/swoole_server/dispatch_mode_7.phpt +++ b/tests/swoole_server/dispatch_mode_7.phpt @@ -13,7 +13,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); @@ -49,9 +49,9 @@ $pm->childFunc = function () use ($pm) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 3, + 'dispatch_mode' => 7, 'package_max_length' => 1024 * 1024 * 2, //2M - "worker_num" => 1, + 'worker_num' => 1, 'log_file' => '/dev/null', "reload_async" => true, )); diff --git a/tests/swoole_server/dispatch_mode_8.phpt b/tests/swoole_server/dispatch_mode_8.phpt index 604aace14a3..07bc726699b 100644 --- a/tests/swoole_server/dispatch_mode_8.phpt +++ b/tests/swoole_server/dispatch_mode_8.phpt @@ -59,7 +59,7 @@ $pm->childFunc = function () use ($pm, $table) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 8, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_CONN_LB, 'package_max_length' => 1024 * 1024 * 2, "worker_num" => 4, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/dispatch_mode_9.phpt b/tests/swoole_server/dispatch_mode_9.phpt index cd64d9f9ee1..1db219de004 100644 --- a/tests/swoole_server/dispatch_mode_9.phpt +++ b/tests/swoole_server/dispatch_mode_9.phpt @@ -79,7 +79,7 @@ $pm->childFunc = function () use ($pm, $table) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 9, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_REQ_LB, 'package_max_length' => 1024 * 1024 * 2, "worker_num" => 4, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/duplicate_registered.phpt b/tests/swoole_server/duplicate_registered.phpt index 591f0af364b..ff2c32949a2 100644 --- a/tests/swoole_server/duplicate_registered.phpt +++ b/tests/swoole_server/duplicate_registered.phpt @@ -5,7 +5,7 @@ swoole_server: duplicate registered --FILE-- on('start', function () { }); Assert::same(true, !!'load Assert'); $mem = null; diff --git a/tests/swoole_server/enable_delay_receive.phpt b/tests/swoole_server/enable_delay_receive.phpt index feaee67ceff..6b0bafa9d51 100644 --- a/tests/swoole_server/enable_delay_receive.phpt +++ b/tests/swoole_server/enable_delay_receive.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'enable_delay_receive' => true, @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { Co::sleep(0.3); $serv->confirm($fd); }); - + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { $serv->send($fd, "hello {$data}"); }); diff --git a/tests/swoole_server/enable_reuse_port.phpt b/tests/swoole_server/enable_reuse_port.phpt index 1f4f86c68c3..e6547713871 100644 --- a/tests/swoole_server/enable_reuse_port.phpt +++ b/tests/swoole_server/enable_reuse_port.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -const N = IS_IN_TRAVIS ? 32 : 128; +const N = IS_IN_CI ? 32 : 128; const W = 4; $pm = new SwooleTest\ProcessManager; diff --git a/tests/swoole_server/eof_protocol.phpt b/tests/swoole_server/eof_protocol.phpt index 3a8e6fcdf9f..83056f5766b 100644 --- a/tests/swoole_server/eof_protocol.phpt +++ b/tests/swoole_server/eof_protocol.phpt @@ -39,7 +39,7 @@ class EofServer extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 2.0)) { exit("connect failed\n"); diff --git a/tests/swoole_server/eof_server.phpt b/tests/swoole_server/eof_server.phpt index 9d5271f4a1b..eb0ff555b6f 100644 --- a/tests/swoole_server/eof_server.phpt +++ b/tests/swoole_server/eof_server.phpt @@ -12,7 +12,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; @@ -44,7 +44,7 @@ $pm->parentFunc = function ($pid) use ($pm) usleep(10000); } echo $client->recv(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/event/before_shutdown.phpt b/tests/swoole_server/event/before_shutdown.phpt index 8a823b1cb99..b8d04e901ac 100644 --- a/tests/swoole_server/event/before_shutdown.phpt +++ b/tests/swoole_server/event/before_shutdown.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/event/manager_start.phpt b/tests/swoole_server/event/manager_start.phpt index 5c024d7e31c..253570ce564 100644 --- a/tests/swoole_server/event/manager_start.phpt +++ b/tests/swoole_server/event/manager_start.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $serv->on("ManagerStart", function (Server $serv) use ($pm) { file_put_contents(FILE, 'manager start'.PHP_EOL); diff --git a/tests/swoole_server/event/manager_stop.phpt b/tests/swoole_server/event/manager_stop.phpt index 77ae5d46c38..a8f3d8c7d2c 100644 --- a/tests/swoole_server/event/manager_stop.phpt +++ b/tests/swoole_server/event/manager_stop.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $atomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_server/event/shutdown.phpt b/tests/swoole_server/event/shutdown.phpt index f8cf89788c3..9c28a5d0d9e 100644 --- a/tests/swoole_server/event/shutdown.phpt +++ b/tests/swoole_server/event/shutdown.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/event/start.phpt b/tests/swoole_server/event/start.phpt index 7cb475e9f9b..ee36e6ffed1 100644 --- a/tests/swoole_server/event/start.phpt +++ b/tests/swoole_server/event/start.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $serv->on("start", function (Server $serv) use ($pm) { file_put_contents(FILE, 'master start'.PHP_EOL); diff --git a/tests/swoole_server/event/worker_exit.phpt b/tests/swoole_server/event/worker_exit.phpt index ac8661859ae..a2f54bef5ac 100644 --- a/tests/swoole_server/event/worker_exit.phpt +++ b/tests/swoole_server/event/worker_exit.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $atomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm, $atomic) { usleep(10000); $serv->shutdown(); } else { - $serv->timer = Timer::tick(100, function () use ($serv, $pm) { + $GLOBALS['timer'] = Timer::tick(100, function () use ($serv, $pm) { $pm->writeLog( 'tick, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); $pm->wakeup(); @@ -66,14 +66,15 @@ $pm->childFunc = function () use ($pm, $atomic) { $serv->on(Constant::EVENT_WORKER_EXIT, function (Server $serv) use ($atomic, $pm) { $pm->writeLog( 'worker exit, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); - Timer::clear($serv->timer); + Timer::clear($GLOBALS['timer']); }); $serv->on(Constant::EVENT_WORKER_STOP, function (Server $serv) use ($pm) { $pm->writeLog('worker stop'); }); - $serv->on("Receive", function () { }); + $serv->on("Receive", function () { + }); $serv->start(); }; diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index c5ccee679e0..b3b63be021e 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -29,22 +29,20 @@ $pm->parentFunc = function ($pid) use ($pm) { $clients[] = $client; } switch_process(); - //reload - echo "[-1] start to reload\n"; + // reload Swoole\Process::kill($pid, SIGUSR1); sleep(3); $pm->kill(); }; $pm->childFunc = function () use ($pm, $atomic) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => WORKER_NUM, 'max_wait_time' => 1, 'enable_coroutine' => false, ]); $server->on('workerStart', function (Swoole\Server $server, $worker_id) use ($pm, $atomic) { - echo "$worker_id [" . $server->worker_pid . "] start\n"; $atomic->add(1); if ($atomic->get() === WORKER_NUM) { $pm->wakeup(); @@ -58,24 +56,15 @@ $pm->childFunc = function () use ($pm, $atomic) { $pm->childFirst(); $pm->run(); +Assert::eq($atomic->get(), WORKER_NUM * 2); ?> --EXPECTF-- -%d [%d] start -%d [%d] start -%d [%d] start -%d [%d] start -%s start to reload [%s] INFO Server is reloading all workers now [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -%d [%d] start -%d [%d] start -%d [%d] start -%d [%d] start -[%s] INFO Server is shutdown now +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index a551f562a5b..039f47e8b87 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -8,38 +8,44 @@ error_reporting(0); require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; +use Swoole\Timer; -$atomic = new swoole_atomic(1); +$atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; +$pm->setWaitTimeout(1000); $pm->parentFunc = function ($pid) use ($pm) { - sleep(2); $pm->kill(); }; -$pm->childFunc = function () use ($pm,$atomic) { - $flag = 0; - $flag1 = 0; +$pm->childFunc = function () use ($pm, $atomic) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ - "worker_num" => 2, - "max_wait_time" => 1, + 'worker_num' => 2, + 'max_wait_time' => 1, 'enable_coroutine' => false, ]); $serv->on("WorkerStart", function (Server $server, $worker_id) use ($pm, $atomic) { - $pm->wakeup(); - echo "$worker_id [".$server->worker_pid."] start \n"; - if ($worker_id == 0 and $atomic->get() == 1) { - $flag = 1; - sleep(10); + echo "$worker_id [".$server->worker_pid."] start\n"; + if ($worker_id == 0 ) { + if ($atomic->get() == 1) { + sleep(10); + } else { + $pm->wakeup(); + } } - if ($worker_id == 1 and $atomic->get() == 1) { - $server->after(1,function() use ($server, $worker_id, $atomic){ + + if ($worker_id == 1 and $atomic->get() == 1) { + Timer::after(1, function () use ($server, $worker_id, $atomic) { $atomic->add(1); - echo "$worker_id [".$server->worker_pid."] start to reload\n"; - $server->reload(); + echo "$worker_id [" . $server->worker_pid . "] reload\n"; + $server->reload(); + }); } }); + $serv->on("WorkerStop", function (Server $server, $worker_id) use ($pm, $atomic) { + echo "$worker_id [".$server->worker_pid."] stop\n"; + }); $serv->on('receive', function ($serv, $fd, $tid, $data) { }); $serv->start(); @@ -49,14 +55,14 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -%s -%s -1 [%s] start to reload -[%s] INFO reload workers -[%s] WARNING ProcessPool::kill_timeout_worker(): force kill worker process(pid=%d, id=%d) -[%s] WARNING ProcessPool::kill_timeout_worker(): force kill worker process(pid=%d, id=%d) -[%s] WARNING ProcessPool::wait(): worker#%d abnormal exit, status=0, signal=9 -[%s] WARNING ProcessPool::wait(): worker#%d abnormal exit, status=0, signal=9 -%s -%s -[%s] INFO Server is shutdown now +%d [%d] start +%d [%d] start +%d [%d] reload +[%s] INFO Server is reloading all workers now +%d [%d] stop +%d [%d] start +[%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=0) exit timeout, force kill the process +[%s] WARNING Worker::report_error(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 +%d [%d] start +%d [%d] stop +%d [%d] stop diff --git a/tests/swoole_server/force_reload3.phpt b/tests/swoole_server/force_reload3.phpt index a9074540d9c..cad1d1cd2d8 100644 --- a/tests/swoole_server/force_reload3.phpt +++ b/tests/swoole_server/force_reload3.phpt @@ -8,8 +8,9 @@ error_reporting(0); require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; +use Swoole\Timer; -$atomic = new swoole_atomic(1); +$atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm,$argv) { @@ -23,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm,$argv) { $pm->childFunc = function () use ($pm,$atomic) { $flag = 0; $flag1 = 0; - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'log_file' => TEST_LOG_FILE, "worker_num" => 2, @@ -32,11 +33,11 @@ $pm->childFunc = function () use ($pm,$atomic) { ]); $serv->on("WorkerStart", function (Server $server, $worker_id) use ($pm, $atomic) { $pm->wakeup(); - $server->after(50,function() use ($server, $worker_id, $atomic){ + Timer::after(50,function() use ($server, $worker_id, $atomic){ if ($atomic->get() == 1) { $atomic->add(1); $server->reload(); - } + } }); }); $serv->on('receive', function ($serv, $fd, $tid, $data) { diff --git a/tests/swoole_server/force_reload4.phpt b/tests/swoole_server/force_reload4.phpt index 821fe0b919c..2867bd845dd 100644 --- a/tests/swoole_server/force_reload4.phpt +++ b/tests/swoole_server/force_reload4.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function () use ($pm) { echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'reload_async' => true, 'task_enable_coroutine' => true, @@ -40,6 +40,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] INFO Server is shutdown now [%s] WARNING %s (ERRNO 9101): worker exit timeout, forced termination OK diff --git a/tests/swoole_server/getCallback.phpt b/tests/swoole_server/getCallback.phpt index a8931503c47..b2c95ead467 100644 --- a/tests/swoole_server/getCallback.phpt +++ b/tests/swoole_server/getCallback.phpt @@ -5,7 +5,7 @@ swoole_server: getCallback --FILE-- on('start', function ($server) { }); $server->on('shutdown', function ($server) { }); Assert::isInstanceOf($server->getCallback('start'), Closure::class); diff --git a/tests/swoole_server/getLastError.phpt b/tests/swoole_server/getLastError.phpt index 332778f002c..1b0c75ae4d3 100644 --- a/tests/swoole_server/getLastError.phpt +++ b/tests/swoole_server/getLastError.phpt @@ -31,7 +31,7 @@ makeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) { Swoole\Timer::clear($timer); echo "SUCCESS\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_server/getSocket.phpt b/tests/swoole_server/getSocket.phpt index d182460f05a..403a0226948 100644 --- a/tests/swoole_server/getSocket.phpt +++ b/tests/swoole_server/getSocket.phpt @@ -7,14 +7,12 @@ swoole_server: getSocket require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine\Client; -use Swoole\Timer; -use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { Co\Run(function () use ($pm) { - $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli = new Client(SWOOLE_SOCK_TCP); $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1); Assert::assert($r); $cli->send("test"); @@ -27,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $socket = $serv->getSocket(); $serv->set([ "worker_num" => 1, @@ -41,7 +39,11 @@ $pm->childFunc = function () use ($pm) { $serv->shutdown(); return; } else { - $serv->send($fd, get_resource_type($socket)); + if (PHP_VERSION_ID > 80000) { + $serv->send($fd, get_class($socket)); + } else { + $serv->send($fd, get_resource_type($socket)); + } } }); $serv->start(); diff --git a/tests/swoole_server/getWorkerStatus.phpt b/tests/swoole_server/getWorkerStatus.phpt index 47fbc53d8d8..9f21a7901f6 100644 --- a/tests/swoole_server/getWorkerStatus.phpt +++ b/tests/swoole_server/getWorkerStatus.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 2, 'log_file' => '/dev/null' @@ -36,8 +36,8 @@ $pm->childFunc = function () use ($pm) { $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { $serv->send($fd, json_encode([ - 'current_worker' => $serv->getWorkerStatus(), - 'another_worker' => $serv->getWorkerStatus(1-$serv->getWorkerId()), + 'current_worker' => $serv->getWorkerStatus(), + 'another_worker' => $serv->getWorkerStatus(1-$serv->getWorkerId()), ])); }); diff --git a/tests/swoole_server/heartbeat_with_base.phpt b/tests/swoole_server/heartbeat_with_base.phpt index 0f60f654cd1..a067505c6b4 100644 --- a/tests/swoole_server/heartbeat_with_base.phpt +++ b/tests/swoole_server/heartbeat_with_base.phpt @@ -8,13 +8,13 @@ skip_if_in_valgrind(); --FILE-- parentFunc = function ($pid) use ($pm) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) - { +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -22,22 +22,20 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($client->recv(), ''); $s2 = time(); Assert::assert($s2 - $s1 > 1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'heartbeat_check_interval' => 1, 'heartbeat_idle_time' => 2, + 'worker_num' => 1, )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); }; diff --git a/tests/swoole_server/idle_worekr_num.phpt b/tests/swoole_server/idle_worekr_num.phpt index 7c4c01e080a..4852a67008c 100644 --- a/tests/swoole_server/idle_worekr_num.phpt +++ b/tests/swoole_server/idle_worekr_num.phpt @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm, $counter) { ]); $serv->on("workerStart", function (Server $serv, $wid) use ($pm, $counter) { if ($counter->add(1) == $serv->setting['worker_num']) { - $serv->defer(function () use ($pm) { + Swoole\Event::defer(function () use ($pm) { $pm->wakeup(); }); } diff --git a/tests/swoole_server/invalid_fd.phpt b/tests/swoole_server/invalid_fd.phpt index d62c90ca268..7681042c50e 100644 --- a/tests/swoole_server/invalid_fd.phpt +++ b/tests/swoole_server/invalid_fd.phpt @@ -12,10 +12,8 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $client = new Co\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); - Assert::assert($client->send('null' . EOF)); - Assert::assert($client->send('-1' . EOF)); - Assert::assert($client->send('100' . EOF)); - Assert::assert($client->send(PHP_INT_MAX . EOF)); + Assert::notEmpty($client->send("TEST" . EOF)); + Assert::notEmpty($client->recv()); switch_process(); $pm->kill(); }); @@ -31,9 +29,11 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { - $to_fd = null; - eval("\$to_fd = ${data};"); - $serv->send($to_fd, "hello {$fd}" . EOF); + Assert::false($serv->send(null, "hello {$fd}")); + Assert::false($serv->send(-1, "hello {$fd}")); + Assert::false($serv->send(100, "hello {$fd}")); + Assert::false($serv->send(PHP_INT_MAX, "hello {$fd}")); + Assert::true($serv->send($fd, "DONE\n")); }); $server->start(); }; @@ -44,5 +44,3 @@ $pm->run(); Warning: Swoole\Server::send(): fd can not be null in %s/tests/swoole_server/invalid_fd.php on line %d Warning: Swoole\Server::send(): invalid fd[-1] in %s/tests/swoole_server/invalid_fd.php on line %d -[%s] NOTICE Server::send_to_connection() (ERRNO 1005): send %d byte failed, session#100 does not exist -[%s] NOTICE Server::send_to_connection() (ERRNO 1005): send %d byte failed, session#9223372036854775807 does not exist diff --git a/tests/swoole_server/invalid_option.phpt b/tests/swoole_server/invalid_option.phpt index af2acc9b73e..8cd5b434dd3 100644 --- a/tests/swoole_server/invalid_option.phpt +++ b/tests/swoole_server/invalid_option.phpt @@ -23,7 +23,7 @@ try { ?> --EXPECTF-- -Warning: unsupported option [invalid_option] in @swoole-src/library/core/Server/Helper.php on line %d -#0 Swoole\Server\Helper::checkOptions() -#1 Swoole\Server\Port->set() -#2 Swoole\Server->set() called at [%s:%d] +Warning: unsupported option [invalid_option] in @swoole/library/core/Server/Helper.php on line %d +%A +%A +%A diff --git a/tests/swoole_server/kill_user_process_01.phpt b/tests/swoole_server/kill_user_process_01.phpt index 1342dab1cf5..ce7fa9a8de2 100644 --- a/tests/swoole_server/kill_user_process_01.phpt +++ b/tests/swoole_server/kill_user_process_01.phpt @@ -31,13 +31,13 @@ $pm->childFunc = function () use ($pm) // $serv->set( // ['log_file' => TEST_LOG_FILE,] // ); - $process2 = new swoole_process(function ($worker) use ($serv, $pm) { + $process2 = new Swoole\Process(function ($worker) use ($serv, $pm) { global $argv; swoole_set_process_name(WORKER_PROC_NAME); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_timer_after(200000, function ($interval) use ($worker, $serv) { + Swoole\Timer::after(200000, function ($interval) use ($worker, $serv) { echo "OK\n"; }); $pm->wakeup(); diff --git a/tests/swoole_server/kill_user_process_02.phpt b/tests/swoole_server/kill_user_process_02.phpt index c5f9ff6a287..72118708949 100644 --- a/tests/swoole_server/kill_user_process_02.phpt +++ b/tests/swoole_server/kill_user_process_02.phpt @@ -31,13 +31,13 @@ $pm->childFunc = function () use ($pm) // $serv->set( // ['log_file' => TEST_LOG_FILE,] // ); - $process2 = new swoole_process(function ($worker) use ($serv, $pm) { + $process2 = new Swoole\Process(function ($worker) use ($serv, $pm) { global $argv; swoole_set_process_name(WORKER_PROC_NAME); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_timer_after(200000, function ($interval) use ($worker, $serv) { + Swoole\Timer::after(200000, function ($interval) use ($worker, $serv) { echo "OK\n"; }); $pm->wakeup(); diff --git a/tests/swoole_server/length/00.phpt b/tests/swoole_server/length/00.phpt index 144014318c0..956ac388e04 100644 --- a/tests/swoole_server/length/00.phpt +++ b/tests/swoole_server/length/00.phpt @@ -35,7 +35,7 @@ class PkgServer extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -68,7 +68,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->close(); usleep(1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/length/01.phpt b/tests/swoole_server/length/01.phpt index 396ee8d9d0c..81b2d53e9a7 100644 --- a/tests/swoole_server/length/01.phpt +++ b/tests/swoole_server/length/01.phpt @@ -33,7 +33,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm, $size) { }; $pm->childFunc = function () use ($pm, $port, $size) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/length/02.phpt b/tests/swoole_server/length/02.phpt index c7cb5235b2d..b48f9740417 100644 --- a/tests/swoole_server/length/02.phpt +++ b/tests/swoole_server/length/02.phpt @@ -38,7 +38,7 @@ class PkgServer_2 extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -63,7 +63,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->close(); usleep(1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/length/03.phpt b/tests/swoole_server/length/03.phpt index 3025eff130c..1a27495093f 100644 --- a/tests/swoole_server/length/03.phpt +++ b/tests/swoole_server/length/03.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm, $size, $_g_data) { }; $pm->childFunc = function () use ($pm, $port, $size, $_g_data) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/length/length_func.phpt b/tests/swoole_server/length/length_func.phpt index 4250de1eb09..112ae523e04 100644 --- a/tests/swoole_server/length/length_func.phpt +++ b/tests/swoole_server/length/length_func.phpt @@ -35,7 +35,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/listen_fail.phpt b/tests/swoole_server/listen_fail.phpt index 052a4d18a4f..96b1b19315e 100644 --- a/tests/swoole_server/listen_fail.phpt +++ b/tests/swoole_server/listen_fail.phpt @@ -5,11 +5,10 @@ swoole_server: listen fail --FILE-- SWOOLE_LOG_NONE]); try { - $serv = new swoole_server('192.0.0.1', 80); -} catch (swoole_exception $e) { + $serv = new Swoole\Server('192.0.0.1', 80, SWOOLE_PROCESS); +} catch (Swoole\Exception $e) { Assert::same($e->getCode(), SOCKET_EADDRNOTAVAIL); echo "DONE\n"; } diff --git a/tests/swoole_server/max_concurrency.phpt b/tests/swoole_server/max_concurrency.phpt deleted file mode 100644 index 2d073abfc6c..00000000000 --- a/tests/swoole_server/max_concurrency.phpt +++ /dev/null @@ -1,74 +0,0 @@ ---TEST-- -swoole_server: max_concurrency ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) { - for ($i=0; $i < 5; $i++) { - go(function () use ($pm, $i) { - $client = new Client(SWOOLE_SOCK_TCP); - $client->set([ - "open_eof_check" => true, - "open_eof_split" => true, - "package_eof" => "\r\n\r\n", - ]); - $r = $client->connect('127.0.0.1', $pm->getFreePort(), -1); - $data = "$i\r\n\r\n"; - $client->send($data); - $ret = $client->recv(); - var_dump(trim($ret)); - $client->close(); - }); - } - - Event::wait(); - - swoole_process::kill($pid); -}; - -$pm->childFunc = function () use ($pm) -{ - Co::set(['max_concurrency' => 1]); - $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); - $serv->set([ - 'worker_num' => 1, - 'dispatch_mode' => 1, - 'open_eof_split' => true, - 'package_eof' => "\r\n\r\n", - 'log_file' => '/dev/null', - ]); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { - $pm->wakeup(); - }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) - { - global $count; - $count = 0; - co::sleep(0.05); - $count += 1; - $serv->send($fd, "$count\r\n\r\n"); - }); - $serv->start(); -}; - -$pm->childFirst(); -$pm->run(); - -?> ---EXPECT-- -string(1) "1" -string(1) "1" -string(1) "1" -string(1) "1" -string(1) "1" diff --git a/tests/swoole_server/max_idle_time_1.phpt b/tests/swoole_server/max_idle_time_1.phpt index 9c335e4c1e8..a9797739a00 100644 --- a/tests/swoole_server/max_idle_time_1.phpt +++ b/tests/swoole_server/max_idle_time_1.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function ($pid) use ($pm, $time1, $time2) { $s = microtime(true); sleep(1); usleep(200000); - Assert::greaterThan($time2->get() - $time1->get(), 1000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); $result = ''; while(true) { $data = $client->recv(); diff --git a/tests/swoole_server/max_idle_time_2.phpt b/tests/swoole_server/max_idle_time_2.phpt index c3247ed0fbb..7d11301fb4d 100644 --- a/tests/swoole_server/max_idle_time_2.phpt +++ b/tests/swoole_server/max_idle_time_2.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm, $time1, $time2) { } sleep(1); usleep(200000); - Assert::greaterThan($time2->get() - $time1->get(), 1000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); $data = $client->recv(); Assert::isEmpty($data); $pm->kill(); diff --git a/tests/swoole_server/max_request.phpt b/tests/swoole_server/max_request.phpt index ee2603ea12e..4dcbefa2655 100644 --- a/tests/swoole_server/max_request.phpt +++ b/tests/swoole_server/max_request.phpt @@ -12,7 +12,7 @@ use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$counter = new swoole_atomic(); +$counter = new Swoole\Atomic(); $pm->parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { @@ -43,12 +43,12 @@ $pm->parentFunc = function ($pid) use ($pm) { global $counter; Assert::assert($counter->get() > 10); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 4, 'dispatch_mode' => 1, diff --git a/tests/swoole_server/max_request_grace_disabled.phpt b/tests/swoole_server/max_request_grace_disabled.phpt index b833d244372..fd0b8f267a5 100644 --- a/tests/swoole_server/max_request_grace_disabled.phpt +++ b/tests/swoole_server/max_request_grace_disabled.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/max_request_grace_enabled.phpt b/tests/swoole_server/max_request_grace_enabled.phpt index 1d07b6c69f1..3d7aa4c20bc 100644 --- a/tests/swoole_server/max_request_grace_enabled.phpt +++ b/tests/swoole_server/max_request_grace_enabled.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/max_request_threshold.phpt b/tests/swoole_server/max_request_threshold.phpt index e50b13afee4..1bc4f62a006 100644 --- a/tests/swoole_server/max_request_threshold.phpt +++ b/tests/swoole_server/max_request_threshold.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/memory_leak/length.phpt b/tests/swoole_server/memory_leak/length.phpt new file mode 100644 index 00000000000..4464a36ff74 --- /dev/null +++ b/tests/swoole_server/memory_leak/length.phpt @@ -0,0 +1,124 @@ +--TEST-- +swoole_server/memory_leak: length +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks, $total) { + $clients = []; + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i, $chunks, &$clients, $total) { + $cli = new Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $count = 0; + foreach ($chunks as $data) { + $count += $cli->send($data); + usleep(10); + } + Assert::eq($count, $total); + $clients[] = $cli; + }); + } + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +phpt_var_dump( + 'total all: ' . number_format(MAX_CONCURRENCY_MID * $total) . + ', n packets: ' . MAX_REQUESTS . + ', n clients: ' . MAX_CONCURRENCY_MID . + ', total: ' . number_format($total) +); + +$pm->childFunc = function () use ($pm, $counter1, $total, $counter2) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('connect', function (Server $serv, $fd, $rid) { + $GLOBALS['bytes_' . $fd] = 0; + $GLOBALS['count_' . $fd] = 0; + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter1, $total, $counter2) { + if ($counter1->get() == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + $counter1->add(strlen($data)); + $counter2->add(); + $GLOBALS['bytes_' . $fd] += strlen($data); + $GLOBALS['count_' . $fd]++; + + if ($GLOBALS['count_' . $fd] == MAX_REQUESTS) { + phpt_var_dump( + 'bytes: ' . number_format($counter1->get()) . + ', count: ' . $counter2->get() . + ', data: ' . strlen($data) . + ', client bytes: ' . number_format($GLOBALS['bytes_' . $fd]) . + ', client count: ' . $GLOBALS['count_' . $fd] + ); + } + + if ($counter1->get() == MAX_CONCURRENCY_MID * $total) { + $pm->wakeup(); + } + }); + $serv->on('close', function (Server $serv, $fd, $rid) { + }); + $serv->on('WorkerStop', function () use ($total, $counter2) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + Assert::eq($counter2->get(), MAX_CONCURRENCY_MID * MAX_REQUESTS); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/memory_leak/pipe_message.phpt b/tests/swoole_server/memory_leak/pipe_message.phpt new file mode 100644 index 00000000000..9829f464c43 --- /dev/null +++ b/tests/swoole_server/memory_leak/pipe_message.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_server/memory_leak: task +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks) { + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($counter1, $counter2, $pm, $total, $chunks) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 2, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv, $wid) use ($pm, $chunks) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + foreach ($chunks as $ch) { + Assert::greaterThan($serv->sendMessage($ch, 1 - $wid), 0); + usleep(10); + } + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) { + + }); + $serv->on('pipeMessage', function (Server $serv, $wid, $data) use ($counter2, $counter1, $pm, $total, $chunks) { + $counter1->add(); + $counter2->add(strlen($data)); + if ($counter2->get() == $total * 2) { + $pm->wakeup(); + } + }); + $serv->on('WorkerStop', function (Server $serv) use ($counter2, $total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/memory_leak/task.phpt b/tests/swoole_server/memory_leak/task.phpt new file mode 100644 index 00000000000..aaa238f0d05 --- /dev/null +++ b/tests/swoole_server/memory_leak/task.phpt @@ -0,0 +1,99 @@ +--TEST-- +swoole_server/memory_leak: task +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks) { + go(function () use ($pm, $chunks) { + $cli = new Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $cli->send("start\n"); + }); + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +$GLOBALS['test_fn'] = function ($taskId, $data, $chunks) { + if ($GLOBALS['counter1'] == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + $GLOBALS['counter1']++; + $GLOBALS['counter2'] += (strlen($data)); + Assert::eq($chunks[$taskId], $data); +}; + +$pm->childFunc = function () use ($pm, $total, $chunks) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 1, + 'task_worker_num' => 1, + 'log_file' => '/dev/null', + )); + $serv->on('WorkerStart', function (Server $serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + $GLOBALS['atomic']->add(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) { + foreach ($chunks as $ch) { + Assert::greaterThanEq($serv->task($ch), 0); + usleep(100); + } + }); + $serv->on('finish', function (Server $serv, $taskId, $data) use ($pm, $total, $chunks) { + $GLOBALS['test_fn']($taskId, $data, $chunks); + if ($GLOBALS['counter2'] == $total) { + $pm->wakeup(); + } + }); + $serv->on('task', function (Server $serv, $taskId, $srcWorkerId, $data) use ($pm, $total, $chunks) { + $GLOBALS['test_fn']($taskId, $data, $chunks); + return $data; + }); + $serv->on('WorkerStop', function (Server $serv) use ($total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + Assert::eq($GLOBALS['counter2'], $total); + $GLOBALS['atomic']->add(); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +Assert::eq($GLOBALS['atomic']->get(), 4); +?> +--EXPECT-- +DONE +DONE diff --git a/tests/swoole_server/memory_leak/tcp.phpt b/tests/swoole_server/memory_leak/tcp.phpt new file mode 100644 index 00000000000..f86ad96f3db --- /dev/null +++ b/tests/swoole_server/memory_leak/tcp.phpt @@ -0,0 +1,82 @@ +--TEST-- +swoole_server/memory_leak: tcp +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + $clients = []; + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i, &$total, $chunks, &$clients) { + $cli = new Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + foreach ($chunks as $data) { + $cli->send($data); + usleep(100); + } + $clients[] = $cli; + }); + } + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $counter, $total) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 1, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('connect', function (Server $serv, $fd, $rid) { + + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter, $total) { + if ($counter->get() == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + if ($counter->add(strlen($data)) == MAX_CONCURRENCY_MID * $total) { + $pm->wakeup(); + } + }); + $serv->on('close', function (Server $serv, $fd, $rid) { + }); + $serv->on('WorkerStop', function () use ($total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/named_parameters.phpt b/tests/swoole_server/named_parameters.phpt new file mode 100644 index 00000000000..7e586fca845 --- /dev/null +++ b/tests/swoole_server/named_parameters.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_server: new twice +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $atomic) { + posix_kill($atomic->get(), SIGINT); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $atomic) { + $http = new Server('0.0.0.0', 9501, SWOOLE_PROCESS); + $http->set([ + Constant::OPTION_WORKER_NUM => 1 + ]); + $http->on('WorkerStart', function () use ($pm, $http, $atomic) { + if ($atomic->get() == 0) { + $atomic->set(posix_getpid()); + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + Swoole\Coroutine\System::waitSignal(SIGINT); + var_dump($http->stop(waitEvent: true), $http->getLastError()); + } else { + $pm->wakeup(); + } + }); + $http->on('request', function () { + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +bool(true) +int(0) diff --git a/tests/swoole_server/new_twice.phpt b/tests/swoole_server/new_twice.phpt index 5ebf9f67730..b7bf7ab6925 100644 --- a/tests/swoole_server/new_twice.phpt +++ b/tests/swoole_server/new_twice.phpt @@ -14,7 +14,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { $fn = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); $cli->send("signal") or die("ERROR"); }; diff --git a/tests/swoole_server/object/pipe_message.phpt b/tests/swoole_server/object/pipe_message.phpt index ee4cd2ccec4..71df29a7eed 100644 --- a/tests/swoole_server/object/pipe_message.phpt +++ b/tests/swoole_server/object/pipe_message.phpt @@ -49,6 +49,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('pipeMessage', function (Server $serv, PipeMessage $msg) { Assert::eq($msg->worker_id, 1 - $serv->getWorkerId()); + Assert::eq($msg->source_worker_id, 1 - $serv->getWorkerId()); $object = $msg->data; $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); }); diff --git a/tests/swoole_server/object/status_info.phpt b/tests/swoole_server/object/status_info.phpt index a79a957bca5..3450052f9f8 100644 --- a/tests/swoole_server/object/status_info.phpt +++ b/tests/swoole_server/object/status_info.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, @@ -83,12 +83,12 @@ $pm->run(); Fatal error: Uncaught RuntimeException: error in %s:%d Stack trace: -#0 [internal function]: {closure}(Object(Swoole\Server), Object(Swoole\Server\PipeMessage)) +#0 [internal function]: {closure%S}(Object(Swoole\Server), Object(Swoole\Server\PipeMessage)) #1 %s(%d): Swoole\Server->start() -#2 [internal function]: {closure}() +#2 [internal function]: {closure%S}() #3 %s(%d): call_user_func(Object(Closure)) #4 %s(%d): SwooleTest\ProcessManager->runChildFunc() -#5 [internal function]: SwooleTest\ProcessManager->SwooleTest\{closure}(Object(Swoole\Process)) +#5 [internal function]: SwooleTest\ProcessManager->%s(Object(Swoole\Process)) #6 %s(%d): Swoole\Process->start() #7 %s(%d): SwooleTest\ProcessManager->run() #8 {main} diff --git a/tests/swoole_server/onReload.phpt b/tests/swoole_server/onReload.phpt index 002247d6a50..7a263028eb2 100644 --- a/tests/swoole_server/onReload.phpt +++ b/tests/swoole_server/onReload.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm,$argv) { }; $pm->childFunc = function () use ($pm, $WorkerStartAtomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'log_file' => TEST_LOG_FILE, "worker_num" => 2, diff --git a/tests/swoole_server/parse_option_to_size.phpt b/tests/swoole_server/parse_option_to_size.phpt new file mode 100644 index 00000000000..b50b825962b --- /dev/null +++ b/tests/swoole_server/parse_option_to_size.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_server: parse option value to size +--SKIPIF-- + +--FILE-- +set([ + 'buffer_output_size' => '2M', +]); +$server->set([ + 'buffer_output_size' => 2 * 1024 * 1024, +]); +$server->set([ + 'buffer_output_size' => 'xxx--2M', +]); +?> +--EXPECTF-- +Fatal 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 + diff --git a/tests/swoole_server/pid_file.phpt b/tests/swoole_server/pid_file.phpt index 8bd2d84a027..41dd782f014 100644 --- a/tests/swoole_server/pid_file.phpt +++ b/tests/swoole_server/pid_file.phpt @@ -5,34 +5,28 @@ swoole_server: pid_file --FILE-- parentFunc = function ($pid) -{ +$pm->parentFunc = function ($pid) { Assert::assert(is_file(PID_FILE)); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'pid_file' => PID_FILE, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("Start", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/reload_base.phpt b/tests/swoole_server/reload_base.phpt deleted file mode 100644 index fcd7a844775..00000000000 --- a/tests/swoole_server/reload_base.phpt +++ /dev/null @@ -1,78 +0,0 @@ ---TEST-- -swoole_server: reload in base mode ---SKIPIF-- - ---FILE-- - new Swoole\Atomic(), - 'task_worker' => new Swoole\Atomic() -]; -$pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function () use ($pm) { - global $counter, $worker_num; - while (!file_exists(TEST_PID_FILE)) { - usleep(100 * 1000); - } - $pid = file_get_contents(TEST_PID_FILE); - $random = mt_rand(1, 12); - usleep(100 * 1000); - for ($n = $random; $n--;) { - Swoole\Process::kill($pid, SIGUSR1); - usleep(100 * 1000); - // Swoole\Process::kill($pid, SIGUSR2); - // usleep(100 * 1000); - } - - /**@var $counter Swoole\Atomic[] */ - $total = $counter['worker']->get() - $worker_num; - $expect = $random * $worker_num; - Assert::same($total, $expect, "[worker reload {$total} but expect {$expect}]"); - - // $total = $counter['task_worker']->get() - 1; - // Assert::same($total, $random * 2, "[task worker reload {$total} but expect {$random}]"); - - $log = file_get_contents(TEST_LOG_FILE); - $log = trim(preg_replace('/.+?\s+?INFO\s+?.+/', '', $log)); - if (!Assert::assert(empty($log))){ - var_dump($log); - } - $pm->kill(); - echo "DONE\n"; -}; -$pm->childFunc = function () use ($pm) { - global $worker_num; - @unlink(TEST_LOG_FILE); - @unlink(TEST_PID_FILE); - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->set([ - 'log_file' => TEST_LOG_FILE, - 'pid_file' => TEST_PID_FILE, - 'worker_num' => $worker_num - // 'task_worker_num' => 1 - ]); - $server->on('ManagerStart', function () use ($pm) { - $pm->wakeup(); - }); - $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { - /**@var $counter Swoole\Atomic[] */ - global $counter; - $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker']; - $atomic->add(1); - }); - $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { }); - // $server->on('Task', function () { }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECTF-- -Notice: SWOOLE_BASE not support reload task workers. in %s on line %d -DONE diff --git a/tests/swoole_server/sendMessage.phpt b/tests/swoole_server/sendMessage.phpt deleted file mode 100644 index a0a7a3febff..00000000000 --- a/tests/swoole_server/sendMessage.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_server: send message ---SKIPIF-- - ---FILE-- -send(opcode_encode("sendMessage", ["SUCCESS", 1])); - Assert::assert($r !== false); -}, function(Client $cli, $recv) { - list($op, $msg) = opcode_decode($recv); - echo $msg; - global $timer; - $cli->close(); - Timer::clear($timer); -}); - -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_server/sendMessage_02.phpt b/tests/swoole_server/sendMessage_02.phpt deleted file mode 100644 index 1cfdcb2d399..00000000000 --- a/tests/swoole_server/sendMessage_02.phpt +++ /dev/null @@ -1,95 +0,0 @@ ---TEST-- -swoole_server: send message [02] ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - $client->set([ - 'package_eof' => "\r\n", - 'open_eof_check' => true, - 'open_eof_split' => true, - ]); - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { - exit("connect failed\n"); - } - $list = []; - for ($i = 0; $i < 7; $i++) - { - $data = $client->recv(); - if ($data === false or $data === '') - { - echo "ERROR\n"; - break; - } - $list[] = intval($data); - } - sort($list); - Assert::same($list, range(0, 6)); - $pm->kill(); -}; - -$pm->childFunc = function () use ($pm) -{ - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP ); - $serv->set([ - 'log_file' => '/dev/null', - 'worker_num' => 4, - 'task_worker_num' => 3, - ]); - - $lock = new swoole\lock(); - - $process = new \Swoole\Process(function ($process) use ($serv) { - while (true) - { - $r = $process->read(); - if (!$r) - { - continue; - } - $cmd = json_decode($r, true); - for ($i = 0; $i < ($serv->setting['worker_num'] + $serv->setting['task_worker_num']); $i++) - { - $serv->sendMessage(['worker_id' => $i, 'fd' => $cmd['fd']], $i); - } - } - }); - - $serv->addProcess($process); - $serv->on("workerStart", function ($serv, $wid) use ($pm) { - if ($wid == 0) { - $pm->wakeup(); - } - }); - $serv->on('connect', function (swoole_server $serv, $fd) use ($process) { - $process->write(json_encode(["fd" => $fd])); - }); - $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { - - }); - - $serv->on('pipeMessage', function (swoole_server $serv, $worker_id, $data) use ($lock) { - //$lock->lock(); - $serv->send($data['fd'], $data['worker_id']."\r\n"); - //$lock->unlock(); - }); - - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) - { - - }); - - $serv->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- diff --git a/tests/swoole_server/sendMessage_1.phpt b/tests/swoole_server/sendMessage_1.phpt new file mode 100644 index 00000000000..3d076b79e75 --- /dev/null +++ b/tests/swoole_server/sendMessage_1.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_server: send message [1] +--SKIPIF-- + +--FILE-- +send(opcode_encode("sendMessage", ["SUCCESS", 1])); + Assert::assert($r !== false); +}, function(Client $cli, $recv) { + list($op, $msg) = opcode_decode($recv); + echo $msg; + global $timer; + $cli->close(); + Timer::clear($timer); +}); + +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_server/sendMessage_2.phpt b/tests/swoole_server/sendMessage_2.phpt new file mode 100644 index 00000000000..6d59834265f --- /dev/null +++ b/tests/swoole_server/sendMessage_2.phpt @@ -0,0 +1,82 @@ +--TEST-- +swoole_server: send message [02] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $list = []; + for ($i = 0; $i < 7; $i++) { + $data = $client->recv(); + if ($data === false or $data === '') { + echo "ERROR\n"; + break; + } + $list[] = intval($data); + } + sort($list); + Assert::same($list, range(0, 6)); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 4, + 'task_worker_num' => 3, + ]); + + $process = new \Swoole\Process(function ($process) use ($serv) { + while (true) { + $r = $process->read(); + if (!$r) { + continue; + } + $cmd = json_decode($r, true); + for ($i = 0; $i < ($serv->setting['worker_num'] + $serv->setting['task_worker_num']); $i++) { + $serv->sendMessage(['worker_id' => $i, 'fd' => $cmd['fd']], $i); + } + } + }); + + $serv->addProcess($process); + $serv->on("workerStart", function ($serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Swoole\Server $serv, $fd) use ($process) { + $process->write(json_encode(["fd" => $fd])); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + }); + + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { + $serv->send($data['fd'], $data['worker_id'] . "\r\n"); + }); + + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { + + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/sendMessage_3.phpt b/tests/swoole_server/sendMessage_3.phpt new file mode 100644 index 00000000000..f4a359c0a96 --- /dev/null +++ b/tests/swoole_server/sendMessage_3.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_server: send message [3] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + echo $client->recv(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + $serv->on("workerStart", function ($serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Swoole\Server $serv, $fd) { + $wid = $serv->getWorkerId(); + $serv->sendMessage([ + 'fd' => $fd, + 'worker_id' => $wid, + 'data' => random_bytes(random_int(1024 * 1024, 2 * 1024 * 1024)), + ], 1 - $wid); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + }); + + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { + $serv->send($data['fd'], "OK\r\n"); + }); + + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { + + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_server/sendMessage_4.phpt b/tests/swoole_server/sendMessage_4.phpt new file mode 100644 index 00000000000..dc9545da9ad --- /dev/null +++ b/tests/swoole_server/sendMessage_4.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_server: send message [4] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + echo $client->recv(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + $serv->on("workerStart", function ($serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Swoole\Server $serv, $fd) { + $wid = $serv->getWorkerId(); + $serv->sendMessage([ + 'fd' => $fd, + 'worker_id' => $wid, + 'exception' => new \Exception(__METHOD__), + ], 1 - $wid); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + }); + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { + $serv->send($data['fd'], "OK\r\n"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_server/sendMessage_in_manager.phpt b/tests/swoole_server/sendMessage_in_manager.phpt new file mode 100644 index 00000000000..1d5752f4845 --- /dev/null +++ b/tests/swoole_server/sendMessage_in_manager.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_server: sendMessage in manager +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $list = []; + swoole_loop_n(N, function () use ($client, &$list) { + $msg = $client->recv(); + $list[] = $msg; + Assert::contains($msg, 'msg-'); + }); + Assert::count($list, N); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + 'task_worker_num' => 2, + ]); + $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($pm) { + $pm->wakeup(); + $pm->wait(); + usleep(10000); + swoole_loop_n(N, function ($i) use ($serv) { + $wid = rand(0, 3); + $serv->sendMessage("msg-" . $i, $wid); + }); + }); + $serv->on(Constant::EVENT_CONNECT, function ($serv, $fd, $reactor_id) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_RECEIVE, function ($serv, $fd, $reactor_id, $data) { + }); + $serv->on(Constant::EVENT_TASK, function ($serv, $fd, $reactor_id, $data) { + }); + $serv->on('pipeMessage', function (Server $serv, $worker_id, $data) { + foreach ($serv->connections as $fd) { + $serv->send($fd, $data . "\r\n"); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/send_2.phpt b/tests/swoole_server/send_2.phpt index 16178eff59d..72fd9da61ba 100644 --- a/tests/swoole_server/send_2.phpt +++ b/tests/swoole_server/send_2.phpt @@ -43,7 +43,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }); } - swoole_event::wait(); + Swoole\Event::wait(); echo $total . " bytes\n"; $pm->kill(); }; diff --git a/tests/swoole_server/send_2m_in_user_process.phpt b/tests/swoole_server/send_2m_in_user_process.phpt index fac2fd372b4..d7c41d485cf 100644 --- a/tests/swoole_server/send_2m_in_user_process.phpt +++ b/tests/swoole_server/send_2m_in_user_process.phpt @@ -56,7 +56,7 @@ $pm->childFunc = function () use ($pm) { 'package_body_offset' => 4, )); - $proc = new swoole\process(function ($process) use ($serv) { + $proc = new Swoole\Process(function ($process) use ($serv) { while (true) { $pkt = $process->read(); if (!$pkt) { diff --git a/tests/swoole_server/send_3.phpt b/tests/swoole_server/send_3.phpt index ec351f36ff5..dd8e0f79b69 100644 --- a/tests/swoole_server/send_3.phpt +++ b/tests/swoole_server/send_3.phpt @@ -40,7 +40,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }); } - swoole_event::wait(); + Swoole\Event::wait(); echo $total . " bytes\n"; $pm->kill(); }; diff --git a/tests/swoole_server/sendfile_02.phpt b/tests/swoole_server/sendfile_02.phpt index c2be1ccf488..45b11456a53 100644 --- a/tests/swoole_server/sendfile_02.phpt +++ b/tests/swoole_server/sendfile_02.phpt @@ -10,7 +10,7 @@ skip_if_extension_not_exist('sockets'); require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm){ - $client = new swoole_client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -35,7 +35,7 @@ $pm->parentFunc = function ($pid) use ($pm){ }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ 'log_file' => '/dev/null', 'kernel_socket_send_buffer_size' => 65536, @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) { + $serv->on('connect', function (Swoole\Server $serv, $fd) { Assert::true($serv->sendfile($fd, TEST_IMAGE)); }); $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { diff --git a/tests/swoole_server/sendfile_ssl.phpt b/tests/swoole_server/sendfile_ssl.phpt index ea787505b7a..44eeb7938ba 100644 --- a/tests/swoole_server/sendfile_ssl.phpt +++ b/tests/swoole_server/sendfile_ssl.phpt @@ -12,7 +12,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -43,7 +43,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ //'log_file' => '/dev/null', 'kernel_socket_send_buffer_size' => 65536, @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) { + $serv->on('connect', function (Swoole\Server $serv, $fd) { Assert::true($serv->sendfile($fd, TEST_IMAGE)); }); $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { diff --git a/tests/swoole_server/sendto_timeout.phpt b/tests/swoole_server/sendto_timeout.phpt index da483b695fb..6293262c09e 100644 --- a/tests/swoole_server/sendto_timeout.phpt +++ b/tests/swoole_server/sendto_timeout.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_DGRAM); + $serv = new Swoole\Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_DGRAM); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_server/shutdown.phpt b/tests/swoole_server/shutdown.phpt index eb2d921e43e..bf7e1d7a18e 100644 --- a/tests/swoole_server/shutdown.phpt +++ b/tests/swoole_server/shutdown.phpt @@ -3,7 +3,6 @@ swoole_server: shutdown --SKIPIF-- --FILE-- ---FILE-- -initRandomData(1); -$pm->parentFunc = function () use ($pm) { - go(function () use ($pm) { - $client = new Co\Client(SWOOLE_SOCK_TCP); - Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); - Assert::assert($client->send($pm->getRandomData()) > 0); - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['worker_num' => mt_rand(2, 4), 'log_file' => '/dev/null']); - $server->on('start', function () use ($pm) { - echo "START\n"; - $pm->wakeup(); - }); - $server->on('receive', function (Swoole\Server $server, int $fd, int $rid, string $data) use ($pm) { - Assert::same($data, $pm->getRandomData()); - $server->shutdown(); - }); - $server->on('shutdown', function () { - echo "SHUTDOWN\n"; - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -$pm->expectExitCode(0); -?> ---EXPECT-- -START -SHUTDOWN diff --git a/tests/swoole_server/shutdown_in_base_single.phpt b/tests/swoole_server/shutdown_in_base_single.phpt deleted file mode 100644 index fbe18bc9be1..00000000000 --- a/tests/swoole_server/shutdown_in_base_single.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_server: shutdown in base single ---SKIPIF-- - ---FILE-- -initRandomData(1); -$pm->parentFunc = function () use ($pm) { - go(function () use ($pm) { - $client = new Co\Client(SWOOLE_SOCK_TCP); - Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); - Assert::assert($client->send($pm->getRandomData()) > 0); - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $server->on('start', function () use ($pm) { - echo "START\n"; - $pm->wakeup(); - }); - $server->on('receive', function (Swoole\Server $server, int $fd, int $rid, string $data) use ($pm) { - Assert::same($data, $pm->getRandomData()); - $server->shutdown(); - }); - $server->on('shutdown', function () { - echo "SHUTDOWN\n"; - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -$pm->expectExitCode(0); -?> ---EXPECT-- -START -SHUTDOWN diff --git a/tests/swoole_server/shutdown_with_base_mode.phpt b/tests/swoole_server/shutdown_with_base_mode.phpt index d09f9a53957..149cf25e221 100644 --- a/tests/swoole_server/shutdown_with_base_mode.phpt +++ b/tests/swoole_server/shutdown_with_base_mode.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/sigint_with_base.phpt b/tests/swoole_server/sigint_with_base.phpt index 5d107058743..4879ddf53cd 100644 --- a/tests/swoole_server/sigint_with_base.phpt +++ b/tests/swoole_server/sigint_with_base.phpt @@ -3,31 +3,34 @@ swoole_server: register sigint handler with base mode --SKIPIF-- --FILE-- parentFunc = function ($pid) use ($pm) { - Swoole\Process::kill($pid, SIGINT); + Process::kill($pid, SIGINT); usleep(10000); echo file_get_contents(TEST_LOG_FILE); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, ]); - $server->on('workerStart', function (Swoole\Server $server) use ($pm) { - $pm->wakeup(); - \Swoole\Process::signal(2, function () use ($server) { + $server->on('workerStart', function (Server $server) use ($pm) { + Process::signal(SIGINT, function () use ($server) { file_put_contents(TEST_LOG_FILE, 'SIGINT, SHUTDOWN' . PHP_EOL); $server->shutdown(); }); + $pm->wakeup(); }); - $server->on('Receive', function (Swoole\Server $server, $fd, $reactorId, $data) { + $server->on('Receive', function (Server $server, $fd, $reactorId, $data) { }); $server->start(); }; diff --git a/tests/swoole_server/single_thread/large_packet.phpt b/tests/swoole_server/single_thread/large_packet.phpt new file mode 100644 index 00000000000..4d793180b77 --- /dev/null +++ b/tests/swoole_server/single_thread/large_packet.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_server/length: package_length_func +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort() . '/'; + $filePath = tempnam('/tmp', 'swoole_test_'); + $rdata = random_bytes(1024 * 1024); + file_put_contents($filePath, $rdata); + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Accept: text/html', + 'Content-Type: multipart/form-data' + ]); + curl_setopt($ch, CURLOPT_POSTFIELDS, [ + 'file' => new CURLFile($filePath, 'text/html') + ]); + $response = curl_exec($ch); + if (curl_errno($ch)) { + echo 'ERROR: ' . curl_error($ch); + } else { + Assert::eq($response, md5($rdata)); + } + curl_close($ch); + unlink($filePath); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'single_thread' => true, + 'worker_num' => 1, + 'dispatch_mode' => 10, + 'package_max_length' => '128m', + ]); + $http->on('WorkerStart', function (Swoole\Http\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function ($request, $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/slow_client.phpt b/tests/swoole_server/slow_client.phpt index 0da0cbafa9f..5a0cf95237c 100644 --- a/tests/swoole_server/slow_client.phpt +++ b/tests/swoole_server/slow_client.phpt @@ -16,7 +16,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); @@ -39,12 +39,12 @@ $pm->parentFunc = function ($pid) use ($port) $bytes += strlen($r); } Assert::same($bytes, N); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm, $port) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) + $serv->on('connect', function (Swoole\Server $serv, $fd) { $_send_data = str_repeat("A", N); $serv->send($fd, $_send_data); diff --git a/tests/swoole_server/slow_master.phpt b/tests/swoole_server/slow_master.phpt index d9768c5d6c3..f7dca07e8ad 100644 --- a/tests/swoole_server/slow_master.phpt +++ b/tests/swoole_server/slow_master.phpt @@ -56,10 +56,10 @@ $pm->childFunc = function () use ($pm, $counter_server, $counter_client, $data_c )); $serv->on(Constant::EVENT_START, function () use ($pm) { - $pm->wakeup(); - Timer::after(50, function (){ + Timer::after(5, function (){ usleep(300000); }); + $pm->wakeup(); }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client, $data_chunks) { diff --git a/tests/swoole_server/ssl/00.phpt b/tests/swoole_server/ssl/00.phpt index 982de974901..2cdadc130a9 100644 --- a/tests/swoole_server/ssl/00.phpt +++ b/tests/swoole_server/ssl/00.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/bad_client.phpt b/tests/swoole_server/ssl/bad_client.phpt index 783bf052f4c..751936651e9 100644 --- a/tests/swoole_server/ssl/bad_client.phpt +++ b/tests/swoole_server/ssl/bad_client.phpt @@ -11,7 +11,7 @@ define('ERROR_FILE', __DIR__.'/ssl_error'); $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set( [ 'log_file' => ERROR_FILE, diff --git a/tests/swoole_server/ssl/dtls.phpt b/tests/swoole_server/ssl/dtls.phpt index 511551153a4..57d62669a87 100644 --- a/tests/swoole_server/ssl/dtls.phpt +++ b/tests/swoole_server/ssl/dtls.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/dtls_big_packet.phpt b/tests/swoole_server/ssl/dtls_big_packet.phpt index da61ed99470..b0a594df4f9 100644 --- a/tests/swoole_server/ssl/dtls_big_packet.phpt +++ b/tests/swoole_server/ssl/dtls_big_packet.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/heartbeat_1.phpt b/tests/swoole_server/ssl/heartbeat_1.phpt index fd584481f07..182c12c97a9 100644 --- a/tests/swoole_server/ssl/heartbeat_1.phpt +++ b/tests/swoole_server/ssl/heartbeat_1.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($client->recv(), ''); $s2 = time(); Assert::assert($s2 - $s1 > 1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/ssl/nodejs.phpt b/tests/swoole_server/ssl/nodejs.phpt index effbc006bc2..8c2d0b57021 100644 --- a/tests/swoole_server/ssl/nodejs.phpt +++ b/tests/swoole_server/ssl/nodejs.phpt @@ -12,7 +12,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { Co\run(function () use ($pm) { - $result = Co::exec('node '.__DIR__.'/code/connect.js '.$pm->getFreePort()); + $result = Co::exec('node '.__DIR__.'/code/client.js '.$pm->getFreePort()); Assert::eq($result['code'], 0); Assert::contains($result['output'], 'swoole-http-server'); }); diff --git a/tests/swoole_server/ssl/verify_01.phpt b/tests/swoole_server/ssl/verify_01.phpt index 02fd081c61a..9ab0bba8f48 100644 --- a/tests/swoole_server/ssl/verify_01.phpt +++ b/tests/swoole_server/ssl/verify_01.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'ssl_cert_file' => __DIR__ . '/../../include/api/ssl-ca/server-cert.pem', 'ssl_key_file' => __DIR__ . '/../../include/api/ssl-ca/server-key.pem', diff --git a/tests/swoole_server/ssl/verify_02.phpt b/tests/swoole_server/ssl/verify_02.phpt index 6ff94a87aee..a4dfbb80d5c 100644 --- a/tests/swoole_server/ssl/verify_02.phpt +++ b/tests/swoole_server/ssl/verify_02.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); $client->set([ 'ssl_cert_file' => SSL_FILE_DIR . '/client.crt', 'ssl_key_file' => SSL_FILE_DIR . '/client.key', @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key', diff --git a/tests/swoole_server/ssl/verify_03.phpt b/tests/swoole_server/ssl/verify_03.phpt index 2f23db272a5..e0e78633749 100644 --- a/tests/swoole_server/ssl/verify_03.phpt +++ b/tests/swoole_server/ssl/verify_03.phpt @@ -13,7 +13,7 @@ $pm = new SwooleTest\ProcessManager; use Swoole\Server; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } diff --git a/tests/swoole_server/start_twice.phpt b/tests/swoole_server/start_twice.phpt index 62ca74dd923..2f3e9c042d7 100644 --- a/tests/swoole_server/start_twice.phpt +++ b/tests/swoole_server/start_twice.phpt @@ -8,14 +8,11 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); $cli->send("signal") or die("ERROR"); }; @@ -24,7 +21,7 @@ $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'enable_coroutine' => false, 'log_file' => '/dev/null', )); diff --git a/tests/swoole_server/stop_in_workerStart.phpt b/tests/swoole_server/stop_in_workerStart.phpt index 9261d1b7ab1..d8585111d8d 100644 --- a/tests/swoole_server/stop_in_workerStart.phpt +++ b/tests/swoole_server/stop_in_workerStart.phpt @@ -8,20 +8,20 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -$server = new Server('127.0.0.1', get_one_free_port()); +$server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1 ]); -$server->on('Receive', function(Server $server, int $fd, int $reactorId, string $data){ +$server->on('Receive', function (Server $server, int $fd, int $reactorId, string $data) { }); -$server->on('WorkerStart', function(Server $server, int $workid){ +$server->on('WorkerStart', function (Server $server, int $workid) { $server->stop(); }); -$server->on('WorkerStop',function(Server $server, int $workid){ +$server->on('WorkerStop', function (Server $server, int $workid) { + usleep(100000); $server->shutdown(); }); $server->start(); ?> --EXPECTF-- -[%s] INFO Server is shutdown now diff --git a/tests/swoole_server/systemd_fds.phpt b/tests/swoole_server/systemd_fds.phpt index e7b34691890..e36d5c3b273 100644 --- a/tests/swoole_server/systemd_fds.phpt +++ b/tests/swoole_server/systemd_fds.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_server: systemd fds --SKIPIF-- - + --FILE-- parentFunc = function ($pid) use ($pm) { $client = new Client($type); Assert::notEmpty($client->connect($host, $port)); $client->send("SUCCESS"); - Assert::eq($client->recv(), 'SUCCESS'.PHP_EOL); + Assert::eq($client->recv(), 'SUCCESS' . PHP_EOL); $client->close(); }; @@ -53,19 +55,19 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { $sockets = []; $start_fd = swoole_array(scandir('/proc/self/fd'))->sort()->last(); - putenv('LISTEN_FDS_START='. $start_fd); + putenv('LISTEN_FDS_START=' . $start_fd); - $sockets[] = stream_socket_server('tcp://127.0.0.1:'.$pm->getFreePort(0), $errno, $errstr); - $sockets[] = stream_socket_server('udp://0.0.0.0:'.$pm->getFreePort(1), $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('tcp://127.0.0.1:' . $pm->getFreePort(0), $errno, $errstr); + $sockets[] = stream_socket_server('udp://0.0.0.0:' . $pm->getFreePort(1), $errno, $errstr, STREAM_SERVER_BIND); if (HAVE_IPV6) { - $sockets[] = stream_socket_server('tcp://[::1]:'.$pm->getFreePort(2), $errno, $errstr); - $sockets[] = stream_socket_server('udp://[::]:'.$pm->getFreePort(3), $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('tcp://[::1]:' . $pm->getFreePort(2), $errno, $errstr); + $sockets[] = stream_socket_server('udp://[::]:' . $pm->getFreePort(3), $errno, $errstr, STREAM_SERVER_BIND); } - $sockets[] = stream_socket_server('unix://'.UNIX_SOCK_1, $errno, $errstr); - $sockets[] = stream_socket_server('udg://'.UNIX_SOCK_2, $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('unix://' . UNIX_SOCK_1, $errno, $errstr); + $sockets[] = stream_socket_server('udg://' . UNIX_SOCK_2, $errno, $errstr, STREAM_SERVER_BIND); - putenv('LISTEN_PID='. posix_getpid()); - putenv('LISTEN_FDS='. count($sockets)); + putenv('LISTEN_PID=' . posix_getpid()); + putenv('LISTEN_FDS=' . count($sockets)); $serv = new Server('SYSTEMD', 0, SWOOLE_BASE); @@ -76,11 +78,11 @@ $pm->childFunc = function () use ($pm) { $serv->on("packet", function (Server $serv, $data, $addr) { // var_dump($addr); - $serv->sendto($addr['address'], isset($addr['port']) ? $addr['port'] : 0, 'SUCCESS'.PHP_EOL); + $serv->sendto($addr['address'], $addr['port'] ?? 0, 'SUCCESS' . PHP_EOL); }); $serv->on("receive", function (Server $serv, $fd, $tid, $data) { - $serv->send($fd, 'SUCCESS'.PHP_EOL); + $serv->send($fd, 'SUCCESS' . PHP_EOL); }); $serv->start(); diff --git a/tests/swoole_server/task/bug_2585.phpt b/tests/swoole_server/task/bug_2585.phpt index 8e5a31fbae5..7b31114bf80 100644 --- a/tests/swoole_server/task/bug_2585.phpt +++ b/tests/swoole_server/task/bug_2585.phpt @@ -16,36 +16,36 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'enable_coroutine' => false, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); if (mt_rand(0, 1)) { $http->task($response->fd); } else { $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); } }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { $fd = $task->data; if (mt_rand(0, 1)) { $task->finish([$fd, 'Hello Swoole!']); } else { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $pdo = new PDO( "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", MYSQL_SERVER_USER, MYSQL_SERVER_PWD @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/callback_is_null.phpt b/tests/swoole_server/task/callback_is_null.phpt index 5beb8fb0a26..76ba6d4fc51 100644 --- a/tests/swoole_server/task/callback_is_null.phpt +++ b/tests/swoole_server/task/callback_is_null.phpt @@ -11,7 +11,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/enable_coroutine.phpt b/tests/swoole_server/task/enable_coroutine.phpt index a06878d5e22..1a9166ee32e 100644 --- a/tests/swoole_server/task/enable_coroutine.phpt +++ b/tests/swoole_server/task/enable_coroutine.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($c->body, "OK"); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; list($ret_n, $ret_random) = $server->taskwait($n, 1); if ($ret_n !== $n) { @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } $response->end('OK'); }); - $server->on('task', function (swoole_http_server $server, Swoole\Server\Task $task) use ($pm, $randoms) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm, $randoms) { Assert::same($task->worker_id, 0); Assert::assert($task->flags > 0); Assert::assert($task->id >= 0); diff --git a/tests/swoole_server/task/finish_timeout.phpt b/tests/swoole_server/task/finish_timeout.phpt index 393c83531de..bc4ab48e951 100644 --- a/tests/swoole_server/task/finish_timeout.phpt +++ b/tests/swoole_server/task/finish_timeout.phpt @@ -24,6 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { + swoole_ignore_error(SWOOLE_ERROR_SERVER_NO_IDLE_WORKER); $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set( [ @@ -64,4 +65,4 @@ unlink(TMP_LOG_FILE); ?> --EXPECTF-- [%s] WARNING Socket::send_blocking(): send %d bytes failed, Error: Resource temporarily unavailable[11] -[%s] WARNING Server::reply_task_result(): send result to worker timed out +[%s] WARNING Server::finish() (ERRNO %d): send result to worker timed out diff --git a/tests/swoole_server/task/huge_data.phpt b/tests/swoole_server/task/huge_data.phpt index 8a718edcd85..a9776df10a5 100644 --- a/tests/swoole_server/task/huge_data.phpt +++ b/tests/swoole_server/task/huge_data.phpt @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) { ]); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); - $scope = IS_IN_TRAVIS ? [4, 16] : [16, 64]; + $scope = IS_IN_CI ? [4, 16] : [16, 64]; $repeat = mt_rand(...$scope); $http->task([ 'fd' => $response->fd, diff --git a/tests/swoole_server/task/kill_01.phpt b/tests/swoole_server/task/kill_01.phpt index 874a6c3e81b..e6e96938868 100644 --- a/tests/swoole_server/task/kill_01.phpt +++ b/tests/swoole_server/task/kill_01.phpt @@ -14,15 +14,14 @@ const PROC_NAME = 'swoole_unittest_server_task_worker'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < 5; $i++) - { - //杀死进程 + for ($i = 0; $i < 5; $i++) { + // 杀死进程 kill_process_by_name(PROC_NAME); usleep(10000); //判断进程是否存在 Assert::assert(get_process_pid_by_name(PROC_NAME) > 0); } - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die("ERROR"); $cli->send("task-01") or die("ERROR"); Assert::same($cli->recv(), "task-01"); diff --git a/tests/swoole_server/task/task_callback.phpt b/tests/swoole_server/task/task_callback.phpt index a70d53791a8..88955a036fc 100644 --- a/tests/swoole_server/task/task_callback.phpt +++ b/tests/swoole_server/task/task_callback.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); // $cli->set(['open_eof_split' => true, 'package_eof' => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); @@ -27,13 +27,13 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($res['message'], 'hello world'); echo "SUCCESS\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_co.phpt b/tests/swoole_server/task/task_co.phpt index 1c026f07863..ede6b91a3be 100644 --- a/tests/swoole_server/task/task_co.phpt +++ b/tests/swoole_server/task/task_co.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 4, @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $pm->wakeup(); }); $server->on('request', - function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; switch ($request->server['path_info']) { case '/task': @@ -62,7 +62,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } } }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $task->finish([$task->data, httpGetBody('http://127.0.0.1:' . $pm->getFreePort() . "/random?n={$task->data}")]); }); $server->on('finish', function () { diff --git a/tests/swoole_server/task/task_enable_coroutine.phpt b/tests/swoole_server/task/task_enable_coroutine.phpt index 762b4e89d9b..fe164537786 100644 --- a/tests/swoole_server/task/task_enable_coroutine.phpt +++ b/tests/swoole_server/task/task_enable_coroutine.phpt @@ -17,35 +17,35 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); if (mt_rand(0, 1)) { $http->task($response->fd); } else { $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); } }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { $fd = $task->data; if (mt_rand(0, 1)) { $task->finish([$fd, 'Hello Swoole!']); } else { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $pdo = new PDO( "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", MYSQL_SERVER_USER, MYSQL_SERVER_PWD @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/task_enable_coroutine_return.phpt b/tests/swoole_server/task/task_enable_coroutine_return.phpt index 10fa8fa33b9..a0f992c53fc 100644 --- a/tests/swoole_server/task/task_enable_coroutine_return.phpt +++ b/tests/swoole_server/task/task_enable_coroutine_return.phpt @@ -17,26 +17,26 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::assert($ret === 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { defer(function ($data) { if (!empty($data)) { list($task, $result) = $data; @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/task_in_manager.phpt b/tests/swoole_server/task/task_in_manager.phpt index 37da7309dbf..c2d2bc0e4f9 100644 --- a/tests/swoole_server/task/task_in_manager.phpt +++ b/tests/swoole_server/task/task_in_manager.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_in_master.phpt b/tests/swoole_server/task/task_in_master.phpt index a89fcaaa6ea..d56ea6f7953 100644 --- a/tests/swoole_server/task/task_in_master.phpt +++ b/tests/swoole_server/task/task_in_master.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_in_task_worker.phpt b/tests/swoole_server/task/task_in_task_worker.phpt index 71ee123c151..37e6aa8b99c 100644 --- a/tests/swoole_server/task/task_in_task_worker.phpt +++ b/tests/swoole_server/task/task_in_task_worker.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_server/task/task_in_user_process.phpt b/tests/swoole_server/task/task_in_user_process.phpt index 0378b713aee..23ff6e872d1 100644 --- a/tests/swoole_server/task/task_in_user_process.phpt +++ b/tests/swoole_server/task/task_in_user_process.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, @@ -30,18 +30,18 @@ $pm->childFunc = function () use ($pm) $serv->addProcess($process); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) use($pm) + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) use($pm) { Assert::false($serv->finish("OK")); $pm->wakeup(); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_server/task/task_ipc_mode_2.phpt b/tests/swoole_server/task/task_ipc_mode_2.phpt index 0dd44735391..bb92da15b0e 100644 --- a/tests/swoole_server/task/task_ipc_mode_2.phpt +++ b/tests/swoole_server/task/task_ipc_mode_2.phpt @@ -5,31 +5,40 @@ swoole_server/task: task_ipc_mode = 2 --FILE-- parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); $pm->kill(); }); }; -$pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); +$pm->childFunc = function () use ($pm, $atomic) { + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, 'task_worker_num' => 4, + 'worker_num' => 2, 'task_ipc_mode' => 2, 'dispatch_mode' => 2 ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); + $server->on('workerStart', function () use ($pm, $atomic) { + if ($atomic->add() == 6) { + $pm->wakeup(); + } }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Request $request, Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = swoole_http_response::create($fd); + $response = Response::create($fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); diff --git a/tests/swoole_server/task/task_ipc_mode_3.phpt b/tests/swoole_server/task/task_ipc_mode_3.phpt index 55ecf552de9..a19914e57ff 100644 --- a/tests/swoole_server/task/task_ipc_mode_3.phpt +++ b/tests/swoole_server/task/task_ipc_mode_3.phpt @@ -5,6 +5,11 @@ swoole_server/task: task_ipc_mode = 3 --FILE-- parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { @@ -13,24 +18,27 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); $pm->kill(); }; -$pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); +$pm->childFunc = function () use ($pm, $atomic) { + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, + 'worker_num' => 3, 'task_worker_num' => 4, 'task_ipc_mode' => 3, 'dispatch_mode' => 2 ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); + $server->on('workerStart', function () use ($pm, $atomic) { + if ($atomic->add() == 7) { + $pm->wakeup(); + } }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Request $request, Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = swoole_http_response::create($fd); + $response = Response::create($server, $fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); diff --git a/tests/swoole_server/task/task_max_request.phpt b/tests/swoole_server/task/task_max_request.phpt index eb844a9196b..5b41faafe52 100644 --- a/tests/swoole_server/task/task_max_request.phpt +++ b/tests/swoole_server/task/task_max_request.phpt @@ -17,7 +17,7 @@ $counter3 = new Atomic(); // task num $process = new Swoole\Process(function() { - $serv = new Server('127.0.0.1', get_one_free_port()); + $serv = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'task_max_request' => 200, @@ -25,10 +25,9 @@ $process = new Swoole\Process(function() { 'log_file' => TEST_LOG_FILE, ]); - $serv->on("WorkerStart", function (Server $serv, $worker_id) - { + $serv->on("WorkerStart", function (Server $serv, $worker_id) { if (!$serv->taskworker) { - for($i = 0; $i< N; $i++) { + for ($i = 0; $i < N; $i++) { $serv->task(array('type' => 'php', 'data' => RandStr::gen(100))); } } else { @@ -38,20 +37,17 @@ $process = new Swoole\Process(function() { } }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) - { + $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { $serv->send($fd, "Server: $data"); }); - $serv->on('Task', function ($swooleServer, $task_id, $workerId, $data) - { + $serv->on('Task', function ($swooleServer, $task_id, $workerId, $data) { global $counter1; $counter1->add(1); return json_encode($data); }); - $serv->on('Finish', function (Server $swooleServer, $workerId, $task_data) - { + $serv->on('Finish', function (Server $swooleServer, $workerId, $task_data) { global $counter2; $counter2->add(1); if ($counter2->get() == N) { diff --git a/tests/swoole_server/task/task_queue.phpt b/tests/swoole_server/task/task_queue.phpt index a82bbe3bcf8..634422f4172 100644 --- a/tests/swoole_server/task/task_queue.phpt +++ b/tests/swoole_server/task/task_queue.phpt @@ -10,7 +10,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die("ERROR"); $cli->send("task-01") or die("ERROR"); echo $cli->recv(); diff --git a/tests/swoole_server/task/task_wait.phpt b/tests/swoole_server/task/task_wait.phpt index 0ed2fb3a981..b5e635c396a 100644 --- a/tests/swoole_server/task/task_wait.phpt +++ b/tests/swoole_server/task/task_wait.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($c->body, "OK"); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, @@ -37,7 +37,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; list($ret_n, $ret_random) = $server->taskwait($n, 1); if ($ret_n !== $n) { @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } $response->end('OK'); }); - $server->on('task', function (swoole_http_server $server, int $task_id, int $worker_id, string $n) use ($pm, $randoms) { + $server->on('task', function (Swoole\Http\Server $server, int $task_id, int $worker_id, string $n) use ($pm, $randoms) { return [$n, $randoms[$n]]; }); $server->start(); diff --git a/tests/swoole_server/task/timer.phpt b/tests/swoole_server/task/timer.phpt index 03217ff7880..25bf7146ebe 100644 --- a/tests/swoole_server/task/timer.phpt +++ b/tests/swoole_server/task/timer.phpt @@ -41,18 +41,18 @@ $pm->childFunc = function () use ($pm) { }); $http->on('WorkerStart', function (Server $server, int $workerId) { if ($server->taskworker) { - swoole_timer_after(1, function () use ($server, $workerId) { + Swoole\Timer::after(1, function () use ($server, $workerId) { var_dump("after1 : " . time()); }); // never callback - swoole_timer_after(10000, function () use ($server, $workerId) { + Swoole\Timer::after(10000, function () use ($server, $workerId) { var_dump("after2 : " . time()); }); } }); $http->on('task', function (Server $server, Task $task) { var_dump('begin : ' . time()); - swoole_timer_after(2000, function () use ($server, $task) { + Swoole\Timer::after(2000, function () use ($server, $task) { var_dump('end : ' . time()); Assert::true($server->push($task->data['fd'], "OK")); }); diff --git a/tests/swoole_server/task/unpack.phpt b/tests/swoole_server/task/unpack.phpt new file mode 100644 index 00000000000..3f34472e770 --- /dev/null +++ b/tests/swoole_server/task/unpack.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_server/task: unpack +--SKIPIF-- + +--FILE-- + random_bytes(random_int(16, 2000)), + 'msg' => 'data 3', + 'int' => random_int(1, 9999999), + 'uniq' => uniqid(), +]; +$packed3 = Task::pack($data3); +Assert::same($data3, Task::unpack($packed3)); + +$data4 = [ + 'data' => random_bytes(random_int(9000, 2 * 1024 * 1024)), + 'msg' => 'data 4', + 'int' => random_int(1, 9999999), + 'uniq' => uniqid(), +]; +$packed4 = Task::pack($data4); +Assert::same($data4, Task::unpack($packed4)); +?> +--EXPECT-- diff --git a/tests/swoole_server/task/without_onfinish.phpt b/tests/swoole_server/task/without_onfinish.phpt index b8d964b3a46..8d6c1b9bcab 100644 --- a/tests/swoole_server/task/without_onfinish.phpt +++ b/tests/swoole_server/task/without_onfinish.phpt @@ -13,22 +13,22 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4 ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); $http->task($response->fd); }); $http->on('task', function ($a, $b, $c, string $fd) { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end('Hello Swoole!'); return null; // no on finish? }); diff --git a/tests/swoole_server/taskWaitMulti.phpt b/tests/swoole_server/taskWaitMulti.phpt index e011396533c..5c2de43439b 100644 --- a/tests/swoole_server/taskWaitMulti.phpt +++ b/tests/swoole_server/taskWaitMulti.phpt @@ -6,11 +6,12 @@ swoole_server: taskWaitMulti parentFunc = function ($pid) use ($port) -{ - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$pm->parentFunc = function ($pid) use ($port) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("task-01") or die("ERROR"); @@ -18,34 +19,29 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("task-02") or die("ERROR"); Assert::same($cli->recv(), 'OK'); $cli->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'task_worker_num' => 1, + 'enable_coroutine' => random_int(0, 100) > 50, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'task-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'task-01') { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $results = $serv->taskWaitMulti($tasks, 2); - } - else - { + } else { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); @@ -53,27 +49,21 @@ $pm->childFunc = function () use ($pm, $port) $tasks[] = 0; $results = $serv->taskWaitMulti($tasks, 0.2); } - if (count($results) == 4) - { + if (count($results) == 4) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if ($data == 0) - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if ($data == 0) { usleep(300000); } return $data; }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/taskwait_01.phpt b/tests/swoole_server/taskwait_01.phpt index eccf7a6c279..3accbf21de0 100644 --- a/tests/swoole_server/taskwait_01.phpt +++ b/tests/swoole_server/taskwait_01.phpt @@ -13,9 +13,8 @@ use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$pm->parentFunc = function ($pid) use ($port) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("array-01") or die("ERROR"); @@ -29,81 +28,55 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("timeout") or die("ERROR"); Assert::same($cli->recv(), 'OK'); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'enable_coroutine' => false, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'array-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'array-01') { $res = $serv->taskwait(['type' => 'array', 'value' => $data]); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'array-02') - { + } elseif ($data == 'array-02') { $res = $serv->taskwait(['type' => 'string', 'value' => $data]); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-01') - { + } elseif ($data == 'string-01') { $res = $serv->taskwait('array'); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-02') - { + } elseif ($data == 'string-02') { $res = $serv->taskwait('string'); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { $res = $serv->taskwait('timeout', 0.2); - if ($res === false) - { + if ($res === false) { $res = $serv->taskwait('string', 0.2); - if ($res === "hello world\n") - { + if ($res === "hello world\n") { $serv->send($fd, 'OK'); return; } @@ -112,39 +85,26 @@ $pm->childFunc = function () use ($pm, $port) } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if (is_array($data)) - { - if ($data['type'] == 'array') - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if (is_array($data)) { + if ($data['type'] == 'array') { return array('name' => 'rango', 'year' => 1987); - } - else - { + } else { return "hello world\n"; } - } - else - { - if ($data == 'array') - { + } else { + if ($data == 'array') { return array('name' => 'rango', 'year' => 1987); - } - elseif ($data == 'string') - { + } elseif ($data == 'string') { return "hello world\n"; - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { usleep(300000); return "task timeout\n"; } } }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/taskwait_02.phpt b/tests/swoole_server/taskwait_02.phpt index 014ebdd92d7..92061d9402b 100644 --- a/tests/swoole_server/taskwait_02.phpt +++ b/tests/swoole_server/taskwait_02.phpt @@ -6,14 +6,15 @@ swoole_server: taskwait [coroutine] parentFunc = function ($pid) use ($port) -{ - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$pm->parentFunc = function ($pid) use ($port) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("array-01") or die("ERROR"); @@ -27,82 +28,56 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("timeout") or die("ERROR"); Assert::same($cli->recv(), 'OK'); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); ini_set('display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'log_file' => '/dev/null', 'enable_coroutine' => true, )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'array-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'array-01') { $res = $serv->taskwait(['type' => 'array', 'value' => $data]); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'array-02') - { + } elseif ($data == 'array-02') { $res = $serv->taskwait(['type' => 'string', 'value' => $data]); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-01') - { + } elseif ($data == 'string-01') { $res = $serv->taskwait('array'); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-02') - { + } elseif ($data == 'string-02') { $res = $serv->taskwait('string'); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { $res = $serv->taskwait('timeout', 0.2); - if ($res === false) - { + if ($res === false) { $res = $serv->taskwait('string', 0.2); - if ($res === "hello world\n") - { + if ($res === "hello world\n") { $serv->send($fd, 'OK'); return; } @@ -111,39 +86,26 @@ $pm->childFunc = function () use ($pm, $port) } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if (is_array($data)) - { - if ($data['type'] == 'array') - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if (is_array($data)) { + if ($data['type'] == 'array') { return array('name' => 'rango', 'year' => 1987); - } - else - { + } else { return "hello world\n"; } - } - else - { - if ($data == 'array') - { + } else { + if ($data == 'array') { return array('name' => 'rango', 'year' => 1987); - } - elseif ($data == 'string') - { + } elseif ($data == 'string') { return "hello world\n"; - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { usleep(300000); return "task timeout\n"; } } }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/user_process.phpt b/tests/swoole_server/user_process.phpt index 7d0fc6a8a03..fb2c08f6226 100644 --- a/tests/swoole_server/user_process.phpt +++ b/tests/swoole_server/user_process.phpt @@ -27,15 +27,15 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(strlen($ret), SIZE +4); } $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); - $proc = new swoole\process(function ($process) use ($serv){ + $proc = new Swoole\Process(function ($process) use ($serv){ //echo posix_getpid()."\n"; while(true) { $data = json_decode($process->read(), true); diff --git a/tests/swoole_server/user_process_2.phpt b/tests/swoole_server/user_process_2.phpt index 2d55fbcb703..2a3cbb2d39a 100644 --- a/tests/swoole_server/user_process_2.phpt +++ b/tests/swoole_server/user_process_2.phpt @@ -12,7 +12,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $process = new \Swoole\Process(function ($process) use ($serv, $pm) { for ($i = 0; $i < 5; $i++) { co::sleep(0.02); diff --git a/tests/swoole_server/user_process_force_exit.phpt b/tests/swoole_server/user_process_force_exit.phpt index 557221ff85e..b9b04a472f9 100644 --- a/tests/swoole_server/user_process_force_exit.phpt +++ b/tests/swoole_server/user_process_force_exit.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $server->addProcess(new Swoole\Process(function () { pcntl_signal(SIGTERM, function () { }); - swoole_timer_tick(1000, function () { + Swoole\Timer::tick(1000, function () { }); })); $server->start(); diff --git a/tests/swoole_server_coro/length_1.phpt b/tests/swoole_server_coro/length_1.phpt index efdd96f7524..45c04c65d04 100644 --- a/tests/swoole_server_coro/length_1.phpt +++ b/tests/swoole_server_coro/length_1.phpt @@ -26,12 +26,12 @@ class TestServer_5 extends LengthServer } TestServer_5::$random_bytes = true; -TestServer_5::$pkg_num = IS_IN_TRAVIS ? 1000 : 10000; +TestServer_5::$pkg_num = IS_IN_CI ? 1000 : 10000; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -65,7 +65,7 @@ $pm->childFunc = function () use ($pm) { $serv = new TestServer_5($pm->getFreePort(), false); $serv->start(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_server_coro/ssl.phpt b/tests/swoole_server_coro/ssl.phpt index 593945a2300..497fed99155 100644 --- a/tests/swoole_server_coro/ssl.phpt +++ b/tests/swoole_server_coro/ssl.phpt @@ -12,9 +12,8 @@ use Swoole\Coroutine\Server\Connection; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } $client->send("hello world"); @@ -38,7 +37,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_server_coro/tcp.phpt b/tests/swoole_server_coro/tcp.phpt index 4a0c0162b6e..8a81891c66c 100644 --- a/tests/swoole_server_coro/tcp.phpt +++ b/tests/swoole_server_coro/tcp.phpt @@ -34,7 +34,7 @@ go(function () { echo $conn->recv(); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- world diff --git a/tests/swoole_server_port/connections.phpt b/tests/swoole_server_port/connections.phpt index 839778535fb..6f68228d98f 100644 --- a/tests/swoole_server_port/connections.phpt +++ b/tests/swoole_server_port/connections.phpt @@ -83,7 +83,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $server = new Swoole\WebSocket\Server("0.0.0.0", $pm->getFreePort(0)); + $server = new Swoole\WebSocket\Server("0.0.0.0", $pm->getFreePort(0), SWOOLE_PROCESS); $server->set( [ Constant::OPTION_LOG_FILE => '/dev/null', diff --git a/tests/swoole_server_port/duplicate_registered.phpt b/tests/swoole_server_port/duplicate_registered.phpt index 09182d74df5..fcbc99a56e1 100644 --- a/tests/swoole_server_port/duplicate_registered.phpt +++ b/tests/swoole_server_port/duplicate_registered.phpt @@ -5,7 +5,7 @@ swoole_server_port: duplicate registered --FILE-- on('receive', function () { }); Assert::same(true, !!'load Assert'); $mem = null; diff --git a/tests/swoole_server_port/heartbeat_3.phpt b/tests/swoole_server_port/heartbeat_3.phpt index 25e13d79154..0225856b8ce 100644 --- a/tests/swoole_server_port/heartbeat_3.phpt +++ b/tests/swoole_server_port/heartbeat_3.phpt @@ -36,7 +36,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->on('receive', function ($server, $fd, $reactorId, $data) { $server->send($fd, 'ok'); }); diff --git a/tests/swoole_server_port/http.phpt b/tests/swoole_server_port/http.phpt index 4e824a527cc..ccbca8aeb7e 100644 --- a/tests/swoole_server_port/http.phpt +++ b/tests/swoole_server_port/http.phpt @@ -46,13 +46,13 @@ $pm->parentFunc = function ($pid) use ($pm) } }); - swoole_event_wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->set([ 'open_eof_check' => true, @@ -71,14 +71,14 @@ $pm->childFunc = function () use ($pm) $resp->end("hello swooler\n"); }); - $server->on("WorkerStart", function (\swoole_server $serv) { + $server->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); $server->start(); diff --git a/tests/swoole_server_port/multi_port.phpt b/tests/swoole_server_port/multi_port.phpt index 2bc7459fbc4..f9fbe7e0740 100644 --- a/tests/swoole_server_port/multi_port.phpt +++ b/tests/swoole_server_port/multi_port.phpt @@ -15,7 +15,7 @@ $port1 = get_one_free_port(); $port2 = get_one_free_port(); $port3 = get_one_free_port(); -function makeTcpClient_without_protocol($host, $port, callable $onConnect = null, callable $onReceive = null) +function makeTcpClient_without_protocol($host, $port, ?callable $onConnect = null, ?callable $onReceive = null) { go(function () use ($host, $port, $onConnect, $onReceive) { $cli = new Client(SWOOLE_SOCK_TCP); @@ -58,13 +58,13 @@ $pm->parentFunc = function ($pid) use ($pm, $port1, $port2, $port3) Assert::same((int)$data, $port1); $cli->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm, $port1, $port2, $port3) { - $server = new Server('127.0.0.1', $port1); + $server = new Server('127.0.0.1', $port1, SWOOLE_PROCESS); $server->set(array( 'log_file' => '/dev/null', 'worker_num' => 1, diff --git a/tests/swoole_server_port/sub_handshake.phpt b/tests/swoole_server_port/sub_handshake.phpt index 574478ccef2..d37f611298a 100644 --- a/tests/swoole_server_port/sub_handshake.phpt +++ b/tests/swoole_server_port/sub_handshake.phpt @@ -17,12 +17,12 @@ $pm->parentFunc = function () use ($pm) { $ret = $cli->recv(); var_dump($ret); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $main_server = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); - $main_server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $main_server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $main_server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->write('hello world'); $response->end(); }); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { 'open_http_protocol' => true, 'open_websocket_protocol' => true ]); - $sub_server->on('handshake', function (swoole_http_request $request, swoole_http_response $response) { + $sub_server->on('handshake', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $secWebSocketKey = $request->header['sec-websocket-key']; $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { @@ -60,9 +60,9 @@ $pm->childFunc = function () use ($pm) { $response->end(); return true; }); - $sub_server->on('message', function (swoole_http_server $server, swoole_websocket_frame $frame) { + $sub_server->on('message', function (Swoole\Http\Server $server, Swoole\WebSocket\Frame $frame) { var_dump($frame); - $response = new swoole_websocket_frame; + $response = new Swoole\WebSocket\Frame; $response->data = 'OK'; $server->send($frame->fd, (string)$response); }); diff --git a/tests/swoole_server_port/tcp_eof.phpt b/tests/swoole_server_port/tcp_eof.phpt index 5e2e370de61..c837afe094d 100644 --- a/tests/swoole_server_port/tcp_eof.phpt +++ b/tests/swoole_server_port/tcp_eof.phpt @@ -32,12 +32,12 @@ $pm->parentFunc = function ($pid) use ($pm) goto fail; } echo "OK\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( //'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); diff --git a/tests/swoole_socket_coro/cancel.phpt b/tests/swoole_socket_coro/cancel.phpt index 9f45b53686e..2e8a8954619 100644 --- a/tests/swoole_socket_coro/cancel.phpt +++ b/tests/swoole_socket_coro/cancel.phpt @@ -25,7 +25,7 @@ go(function () use ($socket) { co::sleep(0.1); $socket->cancel(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_socket_coro/complete_test.phpt b/tests/swoole_socket_coro/complete_test.phpt index b0e79abefe3..45918e1b8ae 100644 --- a/tests/swoole_socket_coro/complete_test.phpt +++ b/tests/swoole_socket_coro/complete_test.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm, $port) { echo("client exit\n"); $socket->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $port) { diff --git a/tests/swoole_socket_coro/concurrency.phpt b/tests/swoole_socket_coro/concurrency.phpt index 3a2d0f89ae0..4f6909a1e9c 100644 --- a/tests/swoole_socket_coro/concurrency.phpt +++ b/tests/swoole_socket_coro/concurrency.phpt @@ -51,6 +51,6 @@ for ($i = 0; $i < N; $i++) } }); } -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_socket_coro/construct_parse_args_failed.phpt b/tests/swoole_socket_coro/construct_parse_args_failed.phpt index 1796a457adc..098dd13d53f 100644 --- a/tests/swoole_socket_coro/construct_parse_args_failed.phpt +++ b/tests/swoole_socket_coro/construct_parse_args_failed.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../include/bootstrap.php'; var_dump(new Co\Socket()); ?> --EXPECTF-- -Fatal error: Uncaught ArgumentCountError: Swoole\Coroutine\Socket::__construct() expects at least 2 parameters, 0 given in %s/tests/swoole_socket_coro/construct_parse_args_failed.php:3 +Fatal 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 Stack trace: #0 %s/tests/swoole_socket_coro/construct_parse_args_failed.php(3): Swoole\Coroutine\Socket->__construct() #1 {main} diff --git a/tests/swoole_socket_coro/getBoundCid.phpt b/tests/swoole_socket_coro/getBoundCid.phpt new file mode 100644 index 00000000000..a68590cf4a0 --- /dev/null +++ b/tests/swoole_socket_coro/getBoundCid.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_socket_coro: getBoundCid +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $sock = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + Assert::assert($sock->connect('127.0.0.1', $pm->getFreePort())); + set_socket_coro_buffer_size($sock, 8192); + $write_co = $read_co = -1; + go(function () use ($pm, $sock, &$write_co, &$read_co) { + Co::sleep(0.001); + echo "CLOSE\n"; + Assert::eq($sock->getBoundCid(SWOOLE_EVENT_READ), $read_co); + Assert::eq($sock->getBoundCid(SWOOLE_EVENT_WRITE), $write_co); + $sock->close(); + $pm->kill(); + echo "DONE\n"; + }); + $write_co = go(function () use ($sock) { + echo "SEND\n"; + $size = 16 * 1024 * 1024; + Assert::lessThan($sock->sendAll(str_repeat('S', $size)), $size); + Assert::eq($sock->errCode, SOCKET_ECANCELED); + echo "SEND CLOSED\n"; + }); + $read_co = go(function () use ($sock) { + echo "RECV\n"; + Assert::false($sock->recv(-1)); + Assert::eq($sock->errCode, SOCKET_ECANCELED); + echo "RECV CLOSED\n"; + }); + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($server->listen()); + go(function () use ($pm, $server) { + if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { + switch_process(); + co::sleep(5); + $conn->close(); + } + $server->close(); + }); + $pm->wakeup(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SEND +RECV +CLOSE +SEND CLOSED +RECV CLOSED +DONE diff --git a/tests/swoole_socket_coro/getopt/tcpinfo.phpt b/tests/swoole_socket_coro/getopt/tcpinfo.phpt new file mode 100644 index 00000000000..8c82166175d --- /dev/null +++ b/tests/swoole_socket_coro/getopt/tcpinfo.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_socket_coro/getopt: tcp info +--SKIPIF-- + +--FILE-- +getOption(SOL_TCP, TCP_INFO); + Assert::greaterThan($info['rcv_space'], 0); + Assert::greaterThan($info['rto'], 0); + Assert::greaterThan($info['rtt'], 0); + Assert::greaterThan($info['snd_mss'], 0); + Assert::greaterThan($info['rcv_mss'], 0); + echo "DONE\n"; + }); + Assert::assert(strpos($content, 'map.baidu.com') !== false); +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/icmp.phpt b/tests/swoole_socket_coro/icmp.phpt new file mode 100644 index 00000000000..2e1d2dcfbaa --- /dev/null +++ b/tests/swoole_socket_coro/icmp.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_socket_coro: icmp +--SKIPIF-- + +--FILE-- +connect($host); + $socket->send($package, strlen($package)); + $pkt = $socket->recv(256); + Assert::notEmpty($pkt); +}); +?> +--EXPECT-- diff --git a/tests/swoole_socket_coro/import_1.phpt b/tests/swoole_socket_coro/import_1.phpt new file mode 100644 index 00000000000..6c164796c18 --- /dev/null +++ b/tests/swoole_socket_coro/import_1.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_socket_coro: import 1 +--SKIPIF-- + +--FILE-- +send("test message"); + $sock->close(); + + var_dump(stream_get_contents($s1)); +}); +?> +--EXPECTF-- +string(12) "test message" diff --git a/tests/swoole_socket_coro/import_2.phpt b/tests/swoole_socket_coro/import_2.phpt new file mode 100644 index 00000000000..53abe48fce0 --- /dev/null +++ b/tests/swoole_socket_coro/import_2.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_socket_coro: import 2 +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } + $s = stream_socket_server("udp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND); + var_dump($s); + var_dump(fclose($s)); + try { + Swoole\Coroutine\Socket::import($s); + } catch (TypeError $e) { + echo $e->getMessage(), "\n"; + } + + echo "Done."; +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::import(): Cannot represent a stream of type %s as a Socket Descriptor in %s on line %d +bool(false) +Swoole\Coroutine\Socket::import(): Argument #1 ($stream) must be of type resource, %s given +resource(%d) of type (stream) +bool(true) +Swoole\Coroutine\Socket::import(): supplied resource is not a valid stream resource +Done. diff --git a/tests/swoole_socket_coro/import_3.phpt b/tests/swoole_socket_coro/import_3.phpt new file mode 100644 index 00000000000..c395ae55e54 --- /dev/null +++ b/tests/swoole_socket_coro/import_3.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_socket_coro: import 3 +--SKIPIF-- + '224.0.0.23', + "interface" => "lo", +)); +if ($so === false) { + die("SKIP joining group 224.0.0.23 on interface lo failed"); +} +socket_close($s); +?> +--FILE-- +setOption(IPPROTO_IP, MCAST_JOIN_GROUP, array( + "group" => '224.0.0.23', + "interface" => "lo", + )); + var_dump($so); + + $sendsock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + Assert::isInstanceOf($sock, Swoole\Coroutine\Socket::class); + + $br = socket_bind($sendsock, '127.0.0.1'); + $so = socket_sendto($sendsock, $m = "my message", strlen($m), 0, "224.0.0.23", 58379); + var_dump($so); + + stream_set_blocking($stream, 0); + var_dump(fread($stream, strlen($m))); + echo "Done.\n"; +}); +?> +--EXPECTF-- +bool(true) +int(10) +string(10) "my message" +Done. diff --git a/tests/swoole_socket_coro/import_4.phpt b/tests/swoole_socket_coro/import_4.phpt new file mode 100644 index 00000000000..b4c5a792055 --- /dev/null +++ b/tests/swoole_socket_coro/import_4.phpt @@ -0,0 +1,104 @@ +--TEST-- +swoole_socket_coro: import 4 +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } + echo "\n"; + } + if ($sock !== null) { + echo "socket_set_block "; + try { + print_r(intval(socket_set_block($sock))); + } catch (Error $e) { + echo get_class($e), ": ", $e->getMessage(), "\n"; + } + echo "\n"; + echo "socket_get_option "; + try { + print_r(intval(socket_get_option($sock, SOL_SOCKET, SO_TYPE))); + } catch (Error $e) { + echo get_class($e), ": ", $e->getMessage(), "\n"; + } + echo "\n"; + } + echo "\n"; +} + +Co\run(function () { + echo "normal\n"; + $stream0 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock0 = Swoole\Coroutine\Socket::import($stream0); + test($stream0, $sock0); + + echo "\nunset stream\n"; + $stream1 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock1 = Swoole\Coroutine\Socket::import($stream1); + unset($stream1); + test(null, $sock1); + + echo "\nunset socket\n"; + $stream2 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock2 = Swoole\Coroutine\Socket::import($stream2); + unset($sock2); + test($stream2, null); + + echo "\nclose stream\n"; + $stream3 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock3 = Swoole\Coroutine\Socket::import($stream3); + fclose($stream3); + test($stream3, $sock3); + + echo "\nclose socket\n"; + $stream4 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock4 = Swoole\Coroutine\Socket::import($stream4); + socket_close($sock4); + test($stream4, $sock4); + + echo "Done.\n"; +}); +?> +--EXPECTF-- +normal +stream_set_blocking 1 +socket_set_block 1 +socket_get_option 2 + + +unset stream +socket_set_block 1 +socket_get_option 2 + + +unset socket +stream_set_blocking 1 + + +close stream +stream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource + +socket_set_block 1 +socket_get_option 2 + + +close socket +stream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource + +socket_set_block 0 +socket_get_option 0 + +Done. diff --git a/tests/swoole_socket_coro/import_5.phpt b/tests/swoole_socket_coro/import_5.phpt new file mode 100644 index 00000000000..135704d5071 --- /dev/null +++ b/tests/swoole_socket_coro/import_5.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_socket_coro: import 5 +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_socket_coro/sendto.phpt b/tests/swoole_socket_coro/sendto.phpt index 4f6ff6b3e0c..a18ca544a37 100644 --- a/tests/swoole_socket_coro/sendto.phpt +++ b/tests/swoole_socket_coro/sendto.phpt @@ -31,7 +31,7 @@ go(function () { echo "[Client] recvfrom[{$peer['address']}:{$peer['port']}] : $data\n"; } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- [Server] recvfrom[127.0.0.1:%d] : hello-0 diff --git a/tests/swoole_socket_coro/sendto_large_packet.phpt b/tests/swoole_socket_coro/sendto_large_packet.phpt index 493b268f1b2..4ea966fa1b3 100644 --- a/tests/swoole_socket_coro/sendto_large_packet.phpt +++ b/tests/swoole_socket_coro/sendto_large_packet.phpt @@ -33,6 +33,6 @@ go(function () { Assert::assert(strlen($data) >= 30000); } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_socket_coro/setopt/multicast.phpt b/tests/swoole_socket_coro/setopt/multicast.phpt index e85b6f99152..d6b315b1cee 100644 --- a/tests/swoole_socket_coro/setopt/multicast.phpt +++ b/tests/swoole_socket_coro/setopt/multicast.phpt @@ -40,7 +40,7 @@ go(function () use ($socket) { } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_socket_coro/ssl.phpt b/tests/swoole_socket_coro/ssl.phpt index 41e3f8dd756..b6cbc730511 100644 --- a/tests/swoole_socket_coro/ssl.phpt +++ b/tests/swoole_socket_coro/ssl.phpt @@ -8,43 +8,12 @@ skip_if_offline(); --FILE-- setProtocol(['open_ssl' => true,]); - - if (!$cli->connect('www.baidu.com', 443)) { - echo "ERROR\n"; - } - - $http = "GET / HTTP/1.1\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Keep-Alive\r\n" - . "Keep-Alive: on\r\n\r\n"; - if (!$cli->send($http)) { - echo "ERROR\n"; - } - - $content = ''; - $length = 0; - while (true) { - $read = $cli->recv(); - if (empty($read)) { - var_dump($read); - break; - } - $content .= $read; - if ($length == 0) { - if (preg_match('#Content-Length: (\d+)#i', $content, $match)) { - $length = intval($match[1]); - } - } - $header_length = strpos($content, "\r\n\r\n"); - if (strlen($content) == $length + $header_length + 4) { - break; - } - } - $cli->close(); + $content = http_get_with_co_socket('www.baidu.com'); Assert::assert(strpos($content, 'map.baidu.com') !== false); }); ?> diff --git a/tests/swoole_socket_coro/unix_stream.phpt b/tests/swoole_socket_coro/unix_stream.phpt new file mode 100644 index 00000000000..ff9fa3de79d --- /dev/null +++ b/tests/swoole_socket_coro/unix_stream.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_socket_coro: unix stream +--SKIPIF-- + +--FILE-- +bind(SOCK_FILE); + $server->listen(); + + go(function () use ($server) { + while (!$server->isClosed()) { + $conn = $server->accept(); + while ($data = $conn->recv()) { + Assert::same($data, 'hello'); + $conn->send('world'); + } + } + }); + + go(function () use ($server) { + $client = new Swoole\Coroutine\Socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP); + $client->connect(SOCK_FILE); + for ($n = MAX_REQUESTS; $n--;) { + $client->send('hello'); + $data = $client->recv(); + Assert::notEmpty($data); + if (empty($data)) { + break; + } + Assert::same($data, 'world'); + } + $client->close(); + $server->close(); + }); +}); +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_table/foreach.phpt b/tests/swoole_table/foreach.phpt index db4bab4124e..52888b5913b 100644 --- a/tests/swoole_table/foreach.phpt +++ b/tests/swoole_table/foreach.phpt @@ -6,11 +6,11 @@ swoole_table: iterator column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/getMemorySize_1.phpt b/tests/swoole_table/getMemorySize_1.phpt new file mode 100644 index 00000000000..0f316f37a47 --- /dev/null +++ b/tests/swoole_table/getMemorySize_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_table: getMemorySize +--SKIPIF-- + +--FILE-- +getMemorySize(), 0); +$table->column('name', Table::TYPE_STRING, 32); +$table->create(); +Assert::greaterThan($table->getMemorySize(), 0); +?> +--EXPECTF-- diff --git a/tests/swoole_table/int.phpt b/tests/swoole_table/int.phpt index 87cef118c8c..f85e8d02235 100644 --- a/tests/swoole_table/int.phpt +++ b/tests/swoole_table/int.phpt @@ -6,12 +6,12 @@ swoole_table: int column('i8', swoole_table::TYPE_INT, 1); -$table->column('i16', swoole_table::TYPE_INT, 2); -$table->column('i32', swoole_table::TYPE_INT, 4); -$table->column('i64', swoole_table::TYPE_INT, 8); +$table->column('i8', Swoole\Table::TYPE_INT, 1); +$table->column('i16', Swoole\Table::TYPE_INT, 2); +$table->column('i32', Swoole\Table::TYPE_INT, 4); +$table->column('i64', Swoole\Table::TYPE_INT, 8); if (!$table->create()) { diff --git a/tests/swoole_table/key_value.phpt b/tests/swoole_table/key_value.phpt index 8c82b1e6e7c..126a226c479 100644 --- a/tests/swoole_table/key_value.phpt +++ b/tests/swoole_table/key_value.phpt @@ -9,11 +9,11 @@ require __DIR__ . '/../include/bootstrap.php'; const PI = 3.1415926; const NAME = 'rango'; -$table = new swoole_table(65536); +$table = new Swoole\Table(65536); -$table->column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/negative.phpt b/tests/swoole_table/negative.phpt index f790a5a49ef..cc8c45b3d17 100644 --- a/tests/swoole_table/negative.phpt +++ b/tests/swoole_table/negative.phpt @@ -6,10 +6,10 @@ swoole_table: negative column('v1', swoole_table::TYPE_INT); -$table->column('v2', swoole_table::TYPE_FLOAT); +$table->column('v1', Swoole\Table::TYPE_INT); +$table->column('v2', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/random_bytes.phpt b/tests/swoole_table/random_bytes.phpt index a34e652444c..a628e1eb459 100644 --- a/tests/swoole_table/random_bytes.phpt +++ b/tests/swoole_table/random_bytes.phpt @@ -5,11 +5,11 @@ swoole_table: read/write random data --FILE-- column('string', \Swoole\Table::TYPE_STRING, 256 * 1024); $table->create(); -$n = IS_IN_TRAVIS ? 100 : 1000; +$n = IS_IN_CI ? 100 : 1000; // $n = 100; $map = []; diff --git a/tests/swoole_table/stats.phpt b/tests/swoole_table/stats.phpt index 2c1887b70e2..6c8097e6358 100644 --- a/tests/swoole_table/stats.phpt +++ b/tests/swoole_table/stats.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Table; -define('N', IS_IN_TRAVIS ? 10000 : 100000); +define('N', IS_IN_CI ? 10000 : 100000); $table = new Table(N); $table->column('string', Table::TYPE_STRING, 256); diff --git a/tests/swoole_table/type_conv.phpt b/tests/swoole_table/type_conv.phpt index a692eb3c7a2..0900f9c58ef 100644 --- a/tests/swoole_table/type_conv.phpt +++ b/tests/swoole_table/type_conv.phpt @@ -9,11 +9,11 @@ require __DIR__ . '/../include/bootstrap.php'; const PI = 3.1415926; const NAME = 'rango'; -$table = new swoole_table(65536); +$table = new Swoole\Table(65536); -$table->column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { echo __LINE__." error"; diff --git a/tests/swoole_thread/add_update.phpt b/tests/swoole_thread/add_update.phpt new file mode 100644 index 00000000000..ace8f1da9e5 --- /dev/null +++ b/tests/swoole_thread/add_update.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_thread: add/update +--SKIPIF-- + +--FILE-- +update(KEY_NOT_EXISTS, $value)); +Assert::true($m->add(KEY_NOT_EXISTS, $value)); +Assert::eq($m[KEY_NOT_EXISTS], $value); + +unset($m[KEY_NOT_EXISTS]); +Assert::eq($m[KEY_NOT_EXISTS], null); + +Assert::false($m->add(KEY_EXISTS, $value)); +Assert::true($m->update(KEY_EXISTS, $value)); +Assert::eq($m[KEY_EXISTS], $value); + +$m2 = new Map(); +$m2[INDEX_EXISTS] = $value; + +Assert::false($m2->update(INDEX_NOT_EXISTS, $value)); +Assert::true($m2->add(INDEX_NOT_EXISTS, $value)); +Assert::eq($m2[INDEX_NOT_EXISTS], $value); + +unset($m2[INDEX_NOT_EXISTS]); +Assert::eq($m2[INDEX_NOT_EXISTS], null); + +Assert::false($m2->add(INDEX_EXISTS, $value)); +Assert::true($m2->update(INDEX_EXISTS, $value)); +Assert::eq($m2[INDEX_EXISTS], $value); + +?> +--EXPECTF-- diff --git a/tests/swoole_thread/affinity.phpt b/tests/swoole_thread/affinity.phpt new file mode 100644 index 00000000000..7516b1b6375 --- /dev/null +++ b/tests/swoole_thread/affinity.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_thread: Affinity +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + $r = Thread::getAffinity(); + Assert::eq(count($r), swoole_cpu_num()); + Assert::assert(Thread::setAffinity([1])); + Assert::eq(Thread::getAffinity(), [1]); + $thread->join(); +}; + +$tm->childFunc = function () { + $r = Thread::getAffinity(); + Assert::eq(count($r), swoole_cpu_num()); + Assert::assert(Thread::setAffinity([0])); + Assert::eq(Thread::getAffinity(), [0]); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/arraylist.phpt b/tests/swoole_thread/arraylist.phpt new file mode 100644 index 00000000000..e7ffe10aa99 --- /dev/null +++ b/tests/swoole_thread/arraylist.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_thread: arraylist +--SKIPIF-- + +--FILE-- +toArray(), $array); +Assert::eq($l->find($uuid), 2); + +for ($i = 0; $i < count($array); $i++) { + Assert::eq($l[$i], $array[$i]); +} + +$array2 = [ + 'key' => 'value', + 'hello' => 'world', +]; +$l[] = $array2; + +Assert::eq(count($l), 5); +Assert::eq($l[4]->toArray(), $array2); + +try { + $l2 = new ArrayList($array2); + echo "never here\n"; +} catch (Throwable $e) { + Assert::contains($e->getMessage(), 'must be an array of type list'); +} + +$uuid2 = uniqid(); +$l[] = $uuid2; +$count = count($l); + +unset($l[1]); +Assert::eq(count($l), $count - 1); +Assert::eq($l[1], $uuid); +Assert::eq($l->find($uuid), 1); +Assert::eq($l->find($uuid2), $count - 2); + +?> +--EXPECTF-- diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt new file mode 100644 index 00000000000..ed12ca48bd9 --- /dev/null +++ b/tests/swoole_thread/async-io.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: async-io +--SKIPIF-- + +--FILE-- +join(); + } + Assert::eq($atomic->get(), C * N); + Assert::eq($atomicLong->get(), C * N * M); +} else { + $id = $args[0]; + $atomic = $args[1]; + $atomicLong = $args[2]; + Co\run(function () use ($atomic, $atomicLong, $md5) { + $n = N; + while ($n--) { + $atomic->add(); + $atomicLong->add(M); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + Assert::eq(md5($rs), $md5); + } + }); + exit(0); +} +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_thread/atomic_ctor.phpt b/tests/swoole_thread/atomic_ctor.phpt new file mode 100644 index 00000000000..e4bbbb5b744 --- /dev/null +++ b/tests/swoole_thread/atomic_ctor.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_thread: atomic ctor +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $num1 = random_int(1, 1 << 31); + $num2 = random_int(1 << 31, PHP_INT_MAX); + $atomic1 = new Swoole\Thread\Atomic($num1); + $atomic2 = new Swoole\Thread\Atomic\Long($num2); + $thread = new Thread(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($lock, $atomic1, $atomic2, $num1, $num2) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + Assert::eq($atomic1->get(), $num1); + Assert::eq($atomic2->get(), $num2); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread diff --git a/tests/swoole_thread/barrier.phpt b/tests/swoole_thread/barrier.phpt new file mode 100644 index 00000000000..1129ab99b5b --- /dev/null +++ b/tests/swoole_thread/barrier.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_thread: barrier +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $barrier = new Barrier(2); + $s = microtime(true); + $thread = new Thread(__FILE__, $barrier); + $barrier->wait(); + Assert::greaterThanEq(microtime(true) - $s, 0.2); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($barrier) { + echo "child thread\n"; + usleep(200_000); + $barrier->wait(); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread diff --git a/tests/swoole_thread/co-stream.phpt b/tests/swoole_thread/co-stream.phpt new file mode 100644 index 00000000000..96d02ec5758 --- /dev/null +++ b/tests/swoole_thread/co-stream.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_thread: co stream +--SKIPIF-- + +--FILE-- +initFreePorts(increment: crc32(__FILE__) % 1000); + +$tm->parentFunc = function () use ($tm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Co\run(function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue); + var_dump('main thread'); + $thread->join(); + }); +}; + +$tm->childFunc = function ($queue) use ($tm) { + var_dump('child thread'); + $fp = $queue->pop(); + Co\run(function () use ($fp, $tm) { + var_dump('child thread, co 0'); + Co\go(function () use ($tm) { + var_dump('child thread, co 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + }); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + }); +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(12) "child thread" +string(18) "child thread, co 0" +string(18) "child thread, co 1" diff --git a/tests/swoole_thread/empty_args.phpt b/tests/swoole_thread/empty_args.phpt new file mode 100644 index 00000000000..8733dbfc040 --- /dev/null +++ b/tests/swoole_thread/empty_args.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_thread: info +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_thread/exit.phpt b/tests/swoole_thread/exit.phpt new file mode 100644 index 00000000000..5e12dc4b7ac --- /dev/null +++ b/tests/swoole_thread/exit.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->unlock(); + $thread->join(); + Assert::eq($thread->getExitStatus(), CODE); + echo 'DONE' . PHP_EOL; +}; + +$tm->childFunc = function ($lock) { + $lock->lock(); + usleep(100_000); + exit(CODE); +}; + +$tm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_thread/fatal_error_1.inc b/tests/swoole_thread/fatal_error_1.inc new file mode 100644 index 00000000000..e332a8b508f --- /dev/null +++ b/tests/swoole_thread/fatal_error_1.inc @@ -0,0 +1,18 @@ +join(); + echo "stop child thread\n"; +} else { + Co\run(function () { + (function () { + swoole_implicit_fn('fatal_error'); + })(); + }); +} +echo "DONE\n"; diff --git a/tests/swoole_thread/fatal_error_1.phpt b/tests/swoole_thread/fatal_error_1.phpt new file mode 100644 index 00000000000..1d9204366ba --- /dev/null +++ b/tests/swoole_thread/fatal_error_1.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_thread: fatal error +--SKIPIF-- + +--FILE-- +getChildOutput(); +Assert::contains($output, "start child thread\n"); +Assert::contains($output, "stop child thread\n"); +Assert::contains($output, "Fatal error: Uncaught Swoole\Error: test"); +?> +--EXPECT-- diff --git a/tests/swoole_thread/fatal_error_2.inc b/tests/swoole_thread/fatal_error_2.inc new file mode 100644 index 00000000000..c0b731d1f14 --- /dev/null +++ b/tests/swoole_thread/fatal_error_2.inc @@ -0,0 +1,16 @@ +join(); + echo "stop child thread\n"; +} else { + (function () { + swoole_implicit_fn('fatal_error'); + })(); +} +echo "DONE\n"; diff --git a/tests/swoole_thread/fatal_error_2.phpt b/tests/swoole_thread/fatal_error_2.phpt new file mode 100644 index 00000000000..2da63c12bbf --- /dev/null +++ b/tests/swoole_thread/fatal_error_2.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_thread: fatal error +--SKIPIF-- + +--FILE-- +getChildOutput(); +Assert::contains($output, "start child thread\n"); +Assert::contains($output, "stop child thread\n"); +Assert::contains($output, "Fatal error: Uncaught Swoole\Error: test"); +?> +--EXPECT-- diff --git a/tests/swoole_thread/fatal_error_3.phpt b/tests/swoole_thread/fatal_error_3.phpt new file mode 100644 index 00000000000..bcd657ad0f8 --- /dev/null +++ b/tests/swoole_thread/fatal_error_3.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_thread: fatal error 3 +--SKIPIF-- + +--FILE-- +parentFunc = function () { + register_shutdown_function(function () { + echo "shutdown\n"; + }); + Assert::eq(Thread::getInfo()['thread_num'], 1); + $thread = new Thread(__FILE__, 'child'); + usleep(100000); + echo "main thread\n"; + Assert::eq(Thread::getInfo()['thread_num'], 2); + $thread->detach(); +}; + +$tm->childFunc = function () { + echo "child thread\n"; + sleep(1000); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread +shutdown +[%s] WARNING PHPCoroutine::enable_hook(): The stream runtime hook must be enabled or disabled only when there are no active threads. +[%s] WARNING php_swoole_thread_rshutdown(): Fatal Error: 2 active threads are running, cannot exit safely. diff --git a/tests/swoole_thread/incr.phpt b/tests/swoole_thread/incr.phpt new file mode 100644 index 00000000000..9c93093f7d8 --- /dev/null +++ b/tests/swoole_thread/incr.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_thread: incr/decr +--SKIPIF-- + +--FILE-- +incr(KEY_NOT_EXISTS_LVAL), 1); +Assert::eq($m[KEY_NOT_EXISTS_LVAL], 1); + +Assert::eq($m->incr(KEY_NOT_EXISTS_DVAL, $add_dval), $add_dval); +Assert::eq($m[KEY_NOT_EXISTS_DVAL], $add_dval); + +Assert::eq($m->incr(KEY_EXISTS_LVAL), $init_lval + 1); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval + 1); + +Assert::eq($m->incr(KEY_EXISTS_DVAL), $init_dval + 1); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval + 1); + +// clean +$m[KEY_EXISTS_LVAL] = $init_lval; +$m[KEY_EXISTS_DVAL] = $init_dval; +unset($m[KEY_NOT_EXISTS_DVAL], $m[KEY_NOT_EXISTS_LVAL]); + +Assert::eq($m->incr(KEY_EXISTS_LVAL, $add_lval), $init_lval + $add_lval); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval + $add_lval); + +Assert::eq($m->incr(KEY_EXISTS_DVAL, $add_lval), $init_dval + $add_lval); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval + $add_lval); + +Assert::eq($m->decr(KEY_NOT_EXISTS_LVAL), -1); +Assert::eq($m[KEY_NOT_EXISTS_LVAL], -1); + +$m[KEY_EXISTS_LVAL] = $init_lval; +$m[KEY_EXISTS_DVAL] = $init_dval; + +Assert::eq($m->decr(KEY_EXISTS_LVAL, $add_lval), $init_lval - $add_lval); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval - $add_lval); + +Assert::eq($m->decr(KEY_EXISTS_DVAL, $add_lval), $init_dval - $add_lval); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval - $add_lval); + +Assert::eq($l->incr(0), 1); +Assert::eq($l[0], 1); + +Assert::eq($l->incr(1, $add_lval), $add_lval); +Assert::eq($l[1], $add_lval); + +$l[0] = 0; +$l[1] = 0; + +Assert::eq($l->incr(0, $add_dval), intval($add_dval)); + +?> +--EXPECTF-- diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt new file mode 100644 index 00000000000..08070ae4608 --- /dev/null +++ b/tests/swoole_thread/info.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_thread: info +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + $info = Thread::getInfo(); + Assert::true($info['is_main_thread']); + $thread->join(); +}; + +$tm->childFunc = function () { + $info = Thread::getInfo(); + Assert::false($info['is_main_thread']); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt new file mode 100644 index 00000000000..03e6301d12f --- /dev/null +++ b/tests/swoole_thread/lock.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($lock) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread diff --git a/tests/swoole_thread/map.phpt b/tests/swoole_thread/map.phpt new file mode 100644 index 00000000000..7abb024f292 --- /dev/null +++ b/tests/swoole_thread/map.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_thread: map +--SKIPIF-- + +--FILE-- + random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => $uuid, + 'd' => time(), +]; + +$m = new Map($array); +Assert::eq($m->toArray(), $array); +Assert::eq(count($m), count($array)); +Assert::eq($m->find($uuid), 'c'); + +foreach ($array as $k => $v) { + Assert::eq($m[$k], $array[$k]); +} + +$array2 = [ + 'key' => 'value', + 'hello' => 'world', +]; +$m['map'] = $array2; +Assert::eq(count($m), 5); +Assert::eq($m['map']->toArray(), $array2); +Assert::eq(count($m['map']), count($array2)); +Assert::eq($m['map']->values(), array_values($array2)); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/map2array.phpt b/tests/swoole_thread/map2array.phpt new file mode 100644 index 00000000000..6682d659861 --- /dev/null +++ b/tests/swoole_thread/map2array.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_thread: map to array +--SKIPIF-- + +--FILE-- + 1111]); +$ls = $LURDATE[$time]->toArray(); +foreach ($ls as $k => $v) { + unset($LURDATE[$time][$k]); +} +unset($LURDATE[$time]); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/name.phpt b/tests/swoole_thread/name.phpt new file mode 100644 index 00000000000..09ce20fb300 --- /dev/null +++ b/tests/swoole_thread/name.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_thread: name +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + Thread::setName('master thread'); + Assert::eq(get_thread_name(), 'master thread'); + $thread->join(); +}; + +$tm->childFunc = function () { + Thread::setName('child thread'); + Assert::eq(get_thread_name(), 'child thread'); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/numeric_strkey.phpt b/tests/swoole_thread/numeric_strkey.phpt new file mode 100644 index 00000000000..e2a0a452bf8 --- /dev/null +++ b/tests/swoole_thread/numeric_strkey.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_thread: numeric key +--SKIPIF-- + +--FILE-- + 2222, 'test' => $uuid]); +Assert::eq($arr[S_KEY], 2222); +Assert::eq($arr[6666], 2222); +Assert::eq($arr['test'], $uuid); + +unset($arr[S_KEY]); +Assert::false(isset($arr[S_KEY])); +Assert::keyNotExists($arr->toArray(), I_KEY); + +$uuid2 = uniqid(); +$arr[6666.66] = $uuid2; +$arr['6666.66'] = $uuid2; +Assert::eq($arr[6666], $uuid2); + +$arr[true] = $uuid2; +$arr[false] = $uuid2; +$arr[null] = $uuid2; + +$stream = fopen('php://stdin', 'r+'); +@$arr[$stream] = $uuid2; + +Assert::eq($arr[true], $uuid2); +Assert::eq($arr[false], $uuid2); +Assert::eq($arr[null], $uuid2); +Assert::eq(@$arr[$stream], $uuid2); + +?> +--EXPECTF-- diff --git a/tests/swoole_thread/php_socket.phpt b/tests/swoole_thread/php_socket.phpt new file mode 100644 index 00000000000..bd0d34d8ce8 --- /dev/null +++ b/tests/swoole_thread/php_socket.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_thread: php_socket +--SKIPIF-- + +--FILE-- +initFreePorts(increment: crc32(__FILE__) % 1000); + +$tm->parentFunc = function () use ($tm) { + $queue = new Queue(); + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); + socket_bind($sock, '127.0.0.1', $tm->getFreePort()); + $queue->push($sock); + $thread = new Thread(__FILE__, $queue, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($queue, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $svr_sock = $queue->pop(); + socket_listen($svr_sock, 128); + $thread = new Thread(__FILE__, $queue, 1); + $conn = socket_accept($svr_sock); + socket_write($conn, "Swoole: hello world\n"); + socket_close($conn); + socket_close($svr_sock); + $thread->join(); + } else { + var_dump('child thread 1'); + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_connect($sock, '127.0.0.1', $tm->getFreePort()); + socket_send($sock, "hello world", 0, 0); + socket_recv($sock, $buf, 1024, 0); + Assert::eq($buf, "Swoole: hello world\n"); + socket_close($sock); + } + exit(0); +}; + +$tm->run(); +echo "Done\n"; +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" +Done diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt new file mode 100644 index 00000000000..10875bffe30 --- /dev/null +++ b/tests/swoole_thread/pipe.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_thread: pipe +--SKIPIF-- + +--FILE-- +recv(8192), $rdata); + $thread->join(); + echo "DONE\n"; + }); +} else { + $socket = $args[0]; + $rdata = $args[1]; + Co\run(function () use ($socket, $rdata, $argv) { + usleep(100); + shell_exec('sleep 0.01'); + $socket->send($rdata); + }); + exit(0); +} +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_thread/priority.phpt b/tests/swoole_thread/priority.phpt new file mode 100644 index 00000000000..8b6354924a6 --- /dev/null +++ b/tests/swoole_thread/priority.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_thread: priority +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + test_thread_priority(10, Thread::SCHED_FIFO); + $thread->join(); +}; + +$tm->childFunc = function () { + test_thread_priority(5, Thread::SCHED_RR); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/putenv.phpt b/tests/swoole_thread/putenv.phpt new file mode 100644 index 00000000000..d681efb310e --- /dev/null +++ b/tests/swoole_thread/putenv.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_thread: putenv +--SKIPIF-- + +--FILE-- +join(); +} + +for ($i = 0; $i < $c; $i++) { + $env = getenv('TEST_THREAD_' . $i); + Assert::notEmpty($env); +} +?> +--EXPECT-- diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt new file mode 100644 index 00000000000..3cc2d73ad02 --- /dev/null +++ b/tests/swoole_thread/queue.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_thread: queue +--SKIPIF-- + +--FILE-- +push($rdata, Queue::NOTIFY_ONE); + usleep(random_int(100, 1000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < C; $i++) { + $threads[$i]->join(); + $total_child += $map[$i]; + } + Assert::eq($total_parent, $total_child); +} else { + $i = $args[0]; + $queue = $args[1]; + $map = $args[2]; + $map[$i] = 0; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + $map[$i] += strlen($job); + Assert::assert(strlen($job), 16); + } + exit(0); +} +?> +--EXPECTF-- diff --git a/tests/swoole_thread/queue_notify_all.phpt b/tests/swoole_thread/queue_notify_all.phpt new file mode 100644 index 00000000000..6cb2f653973 --- /dev/null +++ b/tests/swoole_thread/queue_notify_all.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_thread: queue notify all +--SKIPIF-- + +--FILE-- +wait(); + usleep(10000); + $queue->push($uuid, Queue::NOTIFY_ALL); + for ($i = 0; $i < C; $i++) { + $threads[$i]->join(); + } + Assert::eq($queue->count(), 0); +} else { + $i = $args[0]; + $queue = $args[1]; + $uuid = $args[2]; + $barrier = $args[3]; + $barrier->wait(); + $job = $queue->pop(-1); + if ($job !== null) { + Assert::eq($job, $uuid); + } else { + Assert::eq(swoole_last_error(), SWOOLE_ERROR_NO_PAYLOAD); + } + exit(0); +} +?> +--EXPECTF-- diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt new file mode 100644 index 00000000000..351c496994b --- /dev/null +++ b/tests/swoole_thread/server/base.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + thread_server_test_eof_client($port); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/create_response.phpt b/tests/swoole_thread/server/create_response.phpt new file mode 100644 index 00000000000..59e8f02c152 --- /dev/null +++ b/tests/swoole_thread/server/create_response.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_thread/server: create response +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'task_worker_num' => 3, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 5) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + $resp->detach(); + $serv->task(['fd' => $resp->fd, 'uid' => $req->get['uid']]); +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { + $response = Swoole\Http\Response::create($data['fd']); + $response->end($data['uid']); + $response->close(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + Assert::eq($atomic1->get(), 5); + Assert::eq($atomic2->get(), 5); + echo "shutdown\n"; +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + $reqUid = uniqid(); + Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/exit.phpt b/tests/swoole_thread/server/exit.phpt new file mode 100644 index 00000000000..db62bd78b97 --- /dev/null +++ b/tests/swoole_thread/server/exit.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_thread/server: exit +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_file' => '/dev/null', + 'log_level' => SWOOLE_LOG_ERROR, + 'enable_coroutine' => false, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 2) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } + echo 'worker start', PHP_EOL; +}); +$serv->on('workerError', function (Server $serv, $workerId, $workerPid, $status, $signal) { + Assert::eq($status, CODE); + echo 'worker error', PHP_EOL; +}); +$serv->on('WorkerStop', function (Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/exit') { + swoole_implicit_fn('bailout', CODE); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 3); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $rs = @file_get_contents('http://127.0.0.1:' . $port . '/exit'); + Assert::false($rs); + + usleep(200_000); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +worker start +worker start +begin +worker error +worker start +done +shutdown diff --git a/tests/swoole_thread/server/fatal_error.phpt b/tests/swoole_thread/server/fatal_error.phpt new file mode 100644 index 00000000000..40fe50d7229 --- /dev/null +++ b/tests/swoole_thread/server/fatal_error.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_thread/server: fatal error +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 2) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/error') { + trigger_error('user fatal error', E_USER_ERROR); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 3); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $rs = @file_get_contents('http://127.0.0.1:' . $port . '/error'); + Assert::false($rs); + + usleep(100_000); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin + +Fatal error: user fatal error in %s on line %d +done +shutdown diff --git a/tests/swoole_thread/server/functions.inc b/tests/swoole_thread/server/functions.inc new file mode 100644 index 00000000000..8ffe248537b --- /dev/null +++ b/tests/swoole_thread/server/functions.inc @@ -0,0 +1,12 @@ +set([ + 'open_eof_check' => true, + 'package_eof' => "\r\n", + ]); + Assert::assert($cli->connect('127.0.0.1', $port, 2)); + $cli->send(json_encode(['type' => 'eof']) . "\r\n"); + Assert::eq($cli->recv(), "EOF\r\n"); +} diff --git a/tests/swoole_thread/server/heartbeat.phpt b/tests/swoole_thread/server/heartbeat.phpt new file mode 100644 index 00000000000..d53826cb934 --- /dev/null +++ b/tests/swoole_thread/server/heartbeat.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_thread/server: heartbeat +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 1, + 'log_level' => SWOOLE_LOG_ERROR, + 'heartbeat_check_interval' => 1, + 'heartbeat_idle_time' => 2, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $port, 5, 0)) { + echo "Error: " . $client->errCode; + die("\n"); + } + $s1 = time(); + Assert::same(@$client->recv(), ''); + $s2 = time(); + Assert::assert($s2 - $s1 > 1); + + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/hook_flags.phpt b/tests/swoole_thread/server/hook_flags.phpt new file mode 100644 index 00000000000..3563a1fb2c9 --- /dev/null +++ b/tests/swoole_thread/server/hook_flags.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'hook_flags' => SWOOLE_HOOK_ALL, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Queue(); + $atomic = new Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + $output = file_get_contents("http://127.0.0.1:$port/"); + $queue->push($output, Queue::NOTIFY_ALL); +}); +$serv->on('Request', function ($req, $resp) { + usleep(100000); + $resp->end('DONE'); +}); +$serv->on('shutdown', function ($server) { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), $server->setting['worker_num']); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + for ($i = 0; $i < 4; $i++) { + echo $queue->pop(-1), PHP_EOL; + $atomic->add(1); + } + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +DONE +DONE +DONE +DONE +shutdown diff --git a/tests/swoole_thread/server/listen.phpt b/tests/swoole_thread/server/listen.phpt new file mode 100644 index 00000000000..408077f8a0d --- /dev/null +++ b/tests/swoole_thread/server/listen.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_thread/server: listen +--SKIPIF-- + +--FILE-- +listen('127.0.0.1', $port, SWOOLE_SOCK_TCP); +$serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + Co::join([ + Co\go(function () use ($port) { + thread_server_test_eof_client($port); + }), + Co\go(function () use ($port) { + thread_server_test_eof_client($port + 1); + }) + ]); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/manager_timer.phpt b/tests/swoole_thread/server/manager_timer.phpt new file mode 100644 index 00000000000..76bea5ccd53 --- /dev/null +++ b/tests/swoole_thread/server/manager_timer.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_thread/server: reload +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic1->add(); +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); + $count = 0; + Timer::tick(100, function () use ($queue, &$count) { + $count++; + if ($count == 5) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } + }); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 2); + Assert::eq($atomic2->get(), 2); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +begin 2 +done +shutdown + diff --git a/tests/swoole_thread/server/reload.phpt b/tests/swoole_thread/server/reload.phpt new file mode 100644 index 00000000000..7c4c2857c6e --- /dev/null +++ b/tests/swoole_thread/server/reload.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_thread/server: reload +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 3, + 'task_worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 14) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('beforeReload', function ($serv) { + echo 'beforeReload', PHP_EOL; +}); +$serv->on('afterReload', function ($serv) { + echo 'afterReload', PHP_EOL; + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 14); + Assert::eq($atomic2->get(), 14); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + $serv->reload(); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +beforeReload +afterReload +begin 2 +done +shutdown + diff --git a/tests/swoole_thread/server/reload_task_workers.phpt b/tests/swoole_thread/server/reload_task_workers.phpt new file mode 100644 index 00000000000..f4d2404d5e8 --- /dev/null +++ b/tests/swoole_thread/server/reload_task_workers.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_thread/server: reload task workers +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 3, + 'task_worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $c = $atomic1->add(); + if ($c == 11) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('beforeReload', function ($serv) { + echo 'beforeReload', PHP_EOL; +}); +$serv->on('afterReload', function ($serv) { + echo 'afterReload', PHP_EOL; + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 11); + Assert::eq($atomic2->get(), 11); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + $serv->reload(true); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +beforeReload +afterReload +begin 2 +done +shutdown + diff --git a/tests/swoole_thread/server/reset_concurrency.phpt b/tests/swoole_thread/server/reset_concurrency.phpt new file mode 100644 index 00000000000..e7e33434df2 --- /dev/null +++ b/tests/swoole_thread/server/reset_concurrency.phpt @@ -0,0 +1,119 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_THREAD] +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => WORKER_NUM, + 'max_concurrency' => 160, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == WORKER_NUM) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Server $serv, $workerId) { + echo 'WORKER STOP', PHP_EOL; +}); +$serv->on('pipeMessage', function (Server $serv, $wid, $msg) { + swoole_implicit_fn('bailout'); +}); +$serv->on('Request', function (Request $req, Response $resp) use ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $c = $atomic2->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $serv->stats(); + Assert::eq($stats['concurrency'], N); + $wid = $serv->getWorkerId(); + for ($i = 0; $i < WORKER_NUM; $i++) { + if ($i !== $wid) { + $serv->sendMessage('error', $i); + } + } + swoole_implicit_fn('bailout'); + } else { + $stats = $serv->stats(); + Assert::eq($stats['concurrency'], 1); + $resp->end(json_encode($stats)); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'SHUTDOWN', PHP_EOL; + Assert::eq($atomic1->get(), WORKER_NUM * 2); + Assert::eq($atomic2->get(), N + 1); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv, $port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->pop(-1); + run(function () use ($port, $serv, $atomic1, $queue) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($port) { + $client = new Client('127.0.0.1', $port); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::join($coroutines); + + while (1) { + if ($atomic1->get() == WORKER_NUM * 2) { + break; + } + Co::sleep(0.1); + } + + $client = new Client('127.0.0.1', $port); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + $serv->shutdown(); + + echo "DONE\n"; + }); + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +DONE +WORKER STOP +WORKER STOP +WORKER STOP +WORKER STOP +SHUTDOWN diff --git a/tests/swoole_thread/server/send_in_user_process.phpt b/tests/swoole_thread/server/send_in_user_process.phpt new file mode 100644 index 00000000000..5cd2890512b --- /dev/null +++ b/tests/swoole_thread/server/send_in_user_process.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_thread/server: send in user process +--SKIPIF-- + +--FILE-- +pop(-1); + $reqUid = uniqid(); + Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid); + echo "done\n"; + $serv->shutdown(); +}); +$serv->addProcess($proc); + +$proc2 = new Swoole\Process(function ($process) use ($serv) { + $json = $process->read(); + $data = json_decode($json, true); + $response = Swoole\Http\Response::create($data['fd']); + $response->end($data['uid']); + $response->close(); +}); +$serv->addProcess($proc2); + +$serv->set(array( + 'worker_num' => 1, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + $atomic->add(); + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic] = Thread::getArguments(); + $atomic->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv, $proc2) { + $resp->detach(); + $proc2->write(json_encode(['fd' => $resp->fd, 'uid' => $req->get['uid']])); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + Assert::eq($atomic->get(), 2); + echo "shutdown\n"; +}); + +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt new file mode 100644 index 00000000000..2f277a7f5f0 --- /dev/null +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -0,0 +1,92 @@ +--TEST-- +swoole_thread/server: send large packet +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on("WorkerStop", function (Swoole\Server $serv, $workerId) { +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8); + $serv->send($fd, pack('N', strlen($send_data)) . $send_data); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $c = MAX_CONCURRENCY_LOW; + $n = MAX_REQUESTS_LOW; + + for ($i = 0; $i < $c; $i++) { + go(function () use ($i, $n, $atomic, $port) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + if ($cli->connect('127.0.0.1', $port, 2) == false) { + echo "ERROR\n"; + return; + } + for ($i = 0; $i < $n; $i++) { + $sid = strval(rand(10000000, 99999999)); + $send_data = str_repeat('A', 1000) . $sid; + $cli->send(pack('N', strlen($send_data)) . $send_data); + $data = $cli->recv(); + Assert::same(strlen($data), SIZE); + Assert::same($sid, substr($data, -8, 8)); + } + }); + } + Swoole\Event::wait(); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/setting.phpt b/tests/swoole_thread/server/setting.phpt new file mode 100644 index 00000000000..19f7b531591 --- /dev/null +++ b/tests/swoole_thread/server/setting.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_thread/server: setting +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => N, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + Assert::isArray($serv->setting); + if ($atomic->add(1) == N) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), N); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/stop_worker.phpt b/tests/swoole_thread/server/stop_worker.phpt new file mode 100644 index 00000000000..a2fe64417e0 --- /dev/null +++ b/tests/swoole_thread/server/stop_worker.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_thread/server: stop worker +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'task_worker_num' => 3, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 5) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/stop') { + $serv->stop($req->get['worker'] ?? 0); + $resp->end("OK\n"); + } +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { + +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 7); + Assert::eq($atomic2->get(), 7); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(0, 1)); + echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(2, 4)); + + sleep(1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin +OK +OK +done +shutdown + diff --git a/tests/swoole_thread/server/udp_port.phpt b/tests/swoole_thread/server/udp_port.phpt new file mode 100644 index 00000000000..f6f5c656cfd --- /dev/null +++ b/tests/swoole_thread/server/udp_port.phpt @@ -0,0 +1,76 @@ +--TEST-- +swoole_thread/server: listen udp port +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'reload_async' => true, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$udp = $serv->addListener('127.0.0.1', $port + 1, SWOOLE_SOCK_UDP); +$udp->on('packet', function ($serv, $data, $addr) { + echo "udp packet\n"; + $serv->sendto($addr['address'], $addr['port'], $data); +}); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($atomic->add() == 1) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } + echo "worker start\n"; +}); +$serv->on('message', function (Server $server, $frame) { + echo "message\n"; +}); +$serv->on('workerExit', function (Server $server, $wid) { + var_dump('worker exit: ' . $wid); + Timer::clearAll(); +}); +$serv->on('shutdown', function (Server $server) { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), $server->setting['worker_num']); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + $udp_sock = stream_socket_client('udp://127.0.0.1:' . ($port + 1), $errno, $errstr); + $pkt = random_bytes(1024); + fwrite($udp_sock, $pkt); + $data = fread($udp_sock, 1024); + Assert::eq($pkt, $data); + }); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +worker start +worker start +begin +udp packet +done +shutdown diff --git a/tests/swoole_thread/server/websocket.phpt b/tests/swoole_thread/server/websocket.phpt new file mode 100644 index 00000000000..6409001bbe7 --- /dev/null +++ b/tests/swoole_thread/server/websocket.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_thread/server: websocket +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('message', function (Server $server, $frame) { + $server->push($frame->fd, $frame->data); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + $cli = new Co\Http\Client('127.0.0.1', $port); + $data = base64_decode(random_bytes(2048)); + Assert::assert($cli->upgrade('/')); + $cli->push($data); + $frame = $cli->recv(); + Assert::eq($frame->data, $data); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/shell_exec.phpt b/tests/swoole_thread/shell_exec.phpt new file mode 100644 index 00000000000..f3a7e44920c --- /dev/null +++ b/tests/swoole_thread/shell_exec.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + Assert::true(Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->unlock(); + $thread->join(); + Assert::eq($thread->getExitStatus(), 0); + echo 'DONE' . PHP_EOL; +}; + +$tm->childFunc = function ($lock) { + $lock->lock(); + usleep(100_000); + Co\run(function () { + Assert::true(Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); + shell_exec('ls /tmp'); + sleep(1); + gethostbyname('www.baidu.com'); + }); + exit(0); +}; + +$tm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_thread/signal.phpt b/tests/swoole_thread/signal.phpt new file mode 100644 index 00000000000..7b212326a74 --- /dev/null +++ b/tests/swoole_thread/signal.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_thread: signal +--SKIPIF-- + +--FILE-- +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + $child_pipe = $args[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); + exit(0); +} +?> +--EXPECTF-- +timer +signal term +exit +child exit diff --git a/tests/swoole_thread/sort.phpt b/tests/swoole_thread/sort.phpt new file mode 100644 index 00000000000..99102368737 --- /dev/null +++ b/tests/swoole_thread/sort.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_thread: sort +--SKIPIF-- + +--FILE-- + "lemon", + "o" => "orange", + "O" => "Orange", + "O1" => "Orange1", + "o2" => "orange2", + "O3" => "Orange3", + "o20" => "orange20", + "b" => "banana", +); + +$unsorted_map = new Map($original_map); +$unsorted_map->sort(); + +$copied_map = $original_map; +asort($copied_map); +Assert::eq($unsorted_map->toArray(), $copied_map); + +$original_list = array( 100, 33, 555, 22 ); +$copied_list = $original_list; + +$unsorted_list = new ArrayList($original_list); +$unsorted_list->sort(); +sort($copied_list); +Assert::eq($unsorted_list->toArray(), $copied_list); +?> +--EXPECT-- + diff --git a/tests/swoole_thread/stdio.phpt b/tests/swoole_thread/stdio.phpt new file mode 100644 index 00000000000..318b0c2aff6 --- /dev/null +++ b/tests/swoole_thread/stdio.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_thread: stdio +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->lock(); + $thread->join(); + echo "main thread\n"; +}; + +$tm->childFunc = function ($lock) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + fwrite(STDOUT, "hello swoole\n"); + Assert::notEmpty(STDIN); + exit(0); +}; + +$tm->run(); +echo "DONE\n"; +?> +--EXPECTF-- +child thread +hello swoole +main thread +DONE diff --git a/tests/swoole_thread/stream.phpt b/tests/swoole_thread/stream.phpt new file mode 100644 index 00000000000..e65861c18e8 --- /dev/null +++ b/tests/swoole_thread/stream.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: stream +--SKIPIF-- + +--FILE-- +initFreePorts(increment: crc32(__FILE__) % 1000); + +$tm->parentFunc = function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($queue, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $fp = $queue->pop(); + $thread = new Thread(__FILE__, $queue, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" diff --git a/tests/swoole_thread/stream_arg.phpt b/tests/swoole_thread/stream_arg.phpt new file mode 100644 index 00000000000..7d7f9d0c9ce --- /dev/null +++ b/tests/swoole_thread/stream_arg.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_thread: stream as a thread argument +--SKIPIF-- + +--FILE-- +initFreePorts(increment: crc32(__FILE__) % 1000); + +$tm->parentFunc = function () use ($tm) { + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $thread = new Thread(__FILE__, $fp, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($fp, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $thread = new Thread(__FILE__, $fp, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" diff --git a/tests/swoole_timer/after.phpt b/tests/swoole_timer/after.phpt index fed9f97631f..265b91cd533 100644 --- a/tests/swoole_timer/after.phpt +++ b/tests/swoole_timer/after.phpt @@ -11,14 +11,14 @@ class TimerTest { private $timer_id = null; protected function resetTimer($ms) { - if ($this->timer_id && swoole_timer_exists($this->timer_id)) { - swoole_timer_clear($this->timer_id); + if ($this->timer_id && Swoole\Timer::exists($this->timer_id)) { + Swoole\Timer::clear($this->timer_id); $this->timer_id = null; } if (self::$count == 10) { return; } - $this->timer_id = swoole_timer_after($ms, array($this, 'onTimerTick')); + $this->timer_id = Swoole\Timer::after($ms, array($this, 'onTimerTick')); Assert::assert($this->timer_id > 0); } diff --git a/tests/swoole_timer/bug_2342.phpt b/tests/swoole_timer/bug_2342.phpt index c7021a91baf..d87207a56c6 100644 --- a/tests/swoole_timer/bug_2342.phpt +++ b/tests/swoole_timer/bug_2342.phpt @@ -16,18 +16,18 @@ class workerInfo function worker($timerId, $info) { - swoole_timer_clear($timerId); + Swoole\Timer::clear($timerId); } function manager($timerID) { - swoole_timer_tick( 10, 'worker', new workerInfo()); + Swoole\Timer::tick( 10, 'worker', new workerInfo()); } $mem = memory_get_usage(); -$timerId = swoole_timer_tick(50, 'manager'); -swoole_timer_after(500, function()use($timerId){ - swoole_timer_clear($timerId); +$timerId = Swoole\Timer::tick(50, 'manager'); +Swoole\Timer::after(500, function()use($timerId){ + Swoole\Timer::clear($timerId); }); -swoole_event::wait(); +Swoole\Event::wait(); Assert::assert($mem + 1024 * 1024 * 1 > memory_get_usage()); echo "DONE\n"; diff --git a/tests/swoole_timer/bug_4794.phpt b/tests/swoole_timer/bug_4794.phpt new file mode 100644 index 00000000000..4ff0b446907 --- /dev/null +++ b/tests/swoole_timer/bug_4794.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +push(['rand' => 9999]); + }); + go(function () use ($channel) { + $data = $channel->pop(0.00001); + var_dump($data); + }); +}); +?> +--EXPECT-- +array(1) { + ["rand"]=> + int(9999) +} diff --git a/tests/swoole_timer/bug_4794_2.phpt b/tests/swoole_timer/bug_4794_2.phpt new file mode 100644 index 00000000000..19749e5b77f --- /dev/null +++ b/tests/swoole_timer/bug_4794_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +start(); + +$processSlow = new Process(function () use ($atomic) { + $atomic->wait(10); + usleep(10 * 1000); +}); +$processSlow->start(); + +Coroutine\run(function () use ($processFast, $processSlow, $atomic) { + for ($n = MAX_REQUESTS; $n--;) { + $status = System::waitPid($processSlow->pid, 0.0001); + Assert::false($status); + Assert::same(swoole_last_error(), SOCKET_ETIMEDOUT); + } + $atomic->wakeup(); + $status = System::waitPid($processSlow->pid, 1); + Assert::same($status['pid'], $processSlow->pid); + var_dump($status); + $status = System::waitPid($processFast->pid); + Assert::same($status['pid'], $processFast->pid); + var_dump($status); +}); + +?> +--EXPECTF-- +array(3) { + ["pid"]=> + int(%d) + ["code"]=> + int(0) + ["signal"]=> + int(0) +} +array(3) { + ["pid"]=> + int(%d) + ["code"]=> + int(0) + ["signal"]=> + int(0) +} diff --git a/tests/swoole_timer/bug_4794_3.phpt b/tests/swoole_timer/bug_4794_3.phpt new file mode 100644 index 00000000000..b02852b7277 --- /dev/null +++ b/tests/swoole_timer/bug_4794_3.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_timer/bug_4794_4.phpt b/tests/swoole_timer/bug_4794_4.phpt new file mode 100644 index 00000000000..44be4623ad7 --- /dev/null +++ b/tests/swoole_timer/bug_4794_4.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +wait(); + echo "2\n"; + switch_process(); + Process::kill($pid, SIGUSR1); + $atomic->wait(); + echo "6\n"; + switch_process(); + Process::kill($pid, SIGUSR2); + echo "8\n"; +}); +$killer->start(); + +Coroutine\run(function () use ($atomic) { + Coroutine::sleep(0.001); + switch_process(); + $atomic->wakeup(); + echo "1\n"; + Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1); + echo "3\n"; + Assert::false(System::waitSignal(SIGUSR2, 0.0001)); + echo "4\n"; + $atomic->wakeup(); + echo "5\n"; + Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2); + echo "7\n"; + System::wait(0.0001); + echo "9\n"; +}); + +?> +--EXPECT-- +1 +2 +3 +4 +5 +6 +8 +7 +9 diff --git a/tests/swoole_timer/bug_4794_5.phpt b/tests/swoole_timer/bug_4794_5.phpt new file mode 100644 index 00000000000..4d056fdf197 --- /dev/null +++ b/tests/swoole_timer/bug_4794_5.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_6.phpt b/tests/swoole_timer/bug_4794_6.phpt new file mode 100644 index 00000000000..8db622ec2f7 --- /dev/null +++ b/tests/swoole_timer/bug_4794_6.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80); + $info = $conn->getpeername(); + Assert::eq($info['host'], System::gethostbyname('www.baidu.com'), 'AF_INET', 0.0001); + Assert::eq($info['port'], 80); + } +); +?> +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_7.phpt b/tests/swoole_timer/bug_4794_7.phpt new file mode 100644 index 00000000000..aa3cbed20a1 --- /dev/null +++ b/tests/swoole_timer/bug_4794_7.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +connect("11.11.11.11", 80, 0.0005); +Assert::false($r); +Assert::eq($cli->errCode, SOCKET_ETIMEDOUT); +?> +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_8.phpt b/tests/swoole_timer/bug_4794_8.phpt new file mode 100644 index 00000000000..dab6e17f888 --- /dev/null +++ b/tests/swoole_timer/bug_4794_8.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', 9601)); + Assert::assert($sock->listen(512)); + $conn = $sock->accept(0.0001); + Assert::assert($conn); + Assert::isInstanceOf($conn, Swoole\Coroutine\Socket::class); + + $data = $conn->recv(0.0001); + $json = json_decode($data, true); + Assert::same($json['data'] ?? '', 'hello'); + $conn->send("world\n"); + $conn->close(); +}); + +go(function () { + $conn = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', 9601)); + $conn->send(json_encode(['data' => 'hello'])); + echo $conn->recv(); +}); +?> +--EXPECT-- +world diff --git a/tests/swoole_timer/call_private.phpt b/tests/swoole_timer/call_private.phpt index 56ff632b9e1..408e7574a69 100644 --- a/tests/swoole_timer/call_private.phpt +++ b/tests/swoole_timer/call_private.phpt @@ -13,41 +13,44 @@ class Test private function bar() { } } -swoole_fork_exec(function () { +// method not exists +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [Test::class, 'not_exist']); }); -swoole_fork_exec(function () { +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + 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"'); +} + +// private method +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [Test::class, 'foo']); }); -swoole_fork_exec(function () { +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + Assert::contains($output, 'Swoole\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::foo()'); +} + +// private method +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [new Test, 'bar']); }); +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + Assert::contains($output, 'Swoole\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::bar()'); +} ?> ---EXPECTF-- -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d - -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d - -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d +--EXPECT-- diff --git a/tests/swoole_timer/clearAll.phpt b/tests/swoole_timer/clearAll.phpt index 99efa0a3404..97b4994a898 100644 --- a/tests/swoole_timer/clearAll.phpt +++ b/tests/swoole_timer/clearAll.phpt @@ -5,7 +5,7 @@ swoole_timer: #2697 --FILE-- set(['log_file' => '/dev/null']); $server->on('workerStart', function (Swoole\Server $server, int $worker_id) { Swoole\Timer::after(1000, function () { diff --git a/tests/swoole_timer/enable_coroutine.phpt b/tests/swoole_timer/enable_coroutine.phpt index bbde9e5e63b..ec86768eb37 100644 --- a/tests/swoole_timer/enable_coroutine.phpt +++ b/tests/swoole_timer/enable_coroutine.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; swoole_async_set([ 'enable_coroutine' => false ]); -swoole_timer_after(1, function () { +Swoole\Timer::after(1, function () { $uid = Co::getuid(); echo "#{$uid}\n"; }); diff --git a/tests/swoole_timer/enable_coroutine2.phpt b/tests/swoole_timer/enable_coroutine2.phpt index 44ad8d319e2..18e0d107342 100644 --- a/tests/swoole_timer/enable_coroutine2.phpt +++ b/tests/swoole_timer/enable_coroutine2.phpt @@ -5,20 +5,23 @@ swoole_timer: enable_coroutine setting --FILE-- false ]); Swoole\Timer::after(1, function () { $uid = Co::getuid(); echo "#{$uid}\n"; - Swoole\Timer::set([ - 'enable_coroutine' => true - ]); - Swoole\Timer::after(1, function () { - $uid = Co::getuid(); - echo "#{$uid}\n"; - }); }); +Swoole\Event::wait(); + +swoole_async_set([ + 'enable_coroutine' => true +]); +Swoole\Timer::after(1, function () { + $uid = Co::getuid(); + echo "#{$uid}\n"; +}); +Swoole\Event::wait(); ?> --EXPECT-- #-1 diff --git a/tests/swoole_timer/function_alias.phpt b/tests/swoole_timer/function_alias.phpt new file mode 100644 index 00000000000..870f6eca27d --- /dev/null +++ b/tests/swoole_timer/function_alias.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_timer: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/tests/swoole_timer/greater_than_0.phpt b/tests/swoole_timer/greater_than_0.phpt index 79b5d6c6646..6031fd02b7e 100644 --- a/tests/swoole_timer/greater_than_0.phpt +++ b/tests/swoole_timer/greater_than_0.phpt @@ -6,9 +6,9 @@ swoole_timer: Timer must be greater than 0 --EXPECTF-- diff --git a/tests/swoole_timer/manager.phpt b/tests/swoole_timer/manager.phpt index 3eb7ef27933..99481df1166 100644 --- a/tests/swoole_timer/manager.phpt +++ b/tests/swoole_timer/manager.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server("0.0.0.0", $pm->getFreePort()); + $serv = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( 'worker_num' => 1, @@ -38,21 +38,21 @@ $pm->childFunc = function () use ($pm) file_put_contents(RES_FILE, "start\n", FILE_APPEND); - $id = swoole_timer_tick(300, function () { + $id = Swoole\Timer::tick(300, function () { file_put_contents(RES_FILE, "timer 1\n", FILE_APPEND); }); - swoole_timer_after(900, function () use ($id, $serv, $pm) { + Swoole\Timer::after(900, function () use ($id, $serv, $pm) { file_put_contents(RES_FILE, "timer 2\n", FILE_APPEND); - swoole_timer_clear($id); + Swoole\Timer::clear($id); - swoole_timer_tick(200, function ($id) use ($serv, $pm) { + Swoole\Timer::tick(200, function ($id) use ($serv, $pm) { static $i = 0; file_put_contents(RES_FILE, "timer 3\n", FILE_APPEND); $i ++; if ($i > 4) { file_put_contents(RES_FILE, "end\n", FILE_APPEND); - swoole_timer_clear($id); + Swoole\Timer::clear($id); $pm->wakeup(); $serv->shutdown(); } @@ -60,7 +60,7 @@ $pm->childFunc = function () use ($pm) }); }); - $serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { }); diff --git a/tests/swoole_timer/master.phpt b/tests/swoole_timer/master.phpt index 15413bf2c68..169e149e082 100644 --- a/tests/swoole_timer/master.phpt +++ b/tests/swoole_timer/master.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server("0.0.0.0", $pm->getFreePort()); + $server = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_timer/memory.phpt b/tests/swoole_timer/memory.phpt index 78f18b9e897..5dd7997eb3d 100644 --- a/tests/swoole_timer/memory.phpt +++ b/tests/swoole_timer/memory.phpt @@ -23,7 +23,7 @@ Swoole\Timer::tick(1, function ( $id ) use ( &$stat ) { $stat->m2 = memory_get_usage(); Assert::lessThan($stat->m2 - $stat->m1, 128); echo 'diff[1] ' . ($stat->m2 - $stat->m1) . "\n"; - swoole_timer_clear($id); + Swoole\Timer::clear($id); } }); diff --git a/tests/swoole_timer/reinit_1.phpt b/tests/swoole_timer/reinit_1.phpt index 16978243e5d..49c554f7ff0 100644 --- a/tests/swoole_timer/reinit_1.phpt +++ b/tests/swoole_timer/reinit_1.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server("0.0.0.0", $pm->getFreePort()); + $server = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_timer/swoole_timer_list_alias.phpt b/tests/swoole_timer/swoole_timer_list_alias.phpt new file mode 100644 index 00000000000..320e1a6bc62 --- /dev/null +++ b/tests/swoole_timer/swoole_timer_list_alias.phpt @@ -0,0 +1,11 @@ +--TEST-- +swoole_timer: function alias about swoole_timer_list +--SKIPIF-- + +--FILE-- + +--EXPECT-- +3 diff --git a/tests/swoole_timer/task_worker.phpt b/tests/swoole_timer/task_worker.phpt index 7cb655ab078..6078de33c4c 100644 --- a/tests/swoole_timer/task_worker.phpt +++ b/tests/swoole_timer/task_worker.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); @@ -23,43 +23,43 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'timer']); }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) { + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { list($fd) = $data; - swoole_timer::after(500, function () use ($serv, $fd) { + Swoole\Timer::after(500, function () use ($serv, $fd) { $serv->send($fd, "500\r\n\r\n"); - swoole_timer::after(300, function () use ($serv, $fd) { + Swoole\Timer::after(300, function () use ($serv, $fd) { $serv->send($fd, "800\r\n\r\n"); }); }); - swoole_timer::after(1000, function () use ($serv, $fd) { + Swoole\Timer::after(1000, function () use ($serv, $fd) { $serv->send($fd, "1000[1]\r\n\r\n"); }); - swoole_timer::after(1000, function () use ($serv, $fd) { + Swoole\Timer::after(1000, function () use ($serv, $fd) { $serv->send($fd, "1000[2]\r\n\r\n"); }); - swoole_timer::after(500, function () use ($serv, $fd) { + Swoole\Timer::after(500, function () use ($serv, $fd) { $serv->send($fd, "500[2]\r\n\r\n"); }); - swoole_timer::after(2000, function () use ($serv, $fd) { + Swoole\Timer::after(2000, function () use ($serv, $fd) { $serv->send($fd, "2000\r\n\r\n"); }); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_timer/task_worker_tick_1k.phpt b/tests/swoole_timer/task_worker_tick_1k.phpt index 5963c8643f0..a5a321a51b1 100644 --- a/tests/swoole_timer/task_worker_tick_1k.phpt +++ b/tests/swoole_timer/task_worker_tick_1k.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); @@ -23,23 +23,23 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'timer']); }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) { + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { static $i = 0; - $serv->tick(1, function () use(&$i, $serv) { + Swoole\Timer::tick(1, function () use(&$i, $serv) { $i++; if ($i % 500 == 499) { $serv->send(1, "timer-$i\r\n\r\n"); diff --git a/tests/swoole_websocket_server/bug_1.phpt b/tests/swoole_websocket_server/bug_1.phpt new file mode 100644 index 00000000000..76e164c6fa0 --- /dev/null +++ b/tests/swoole_websocket_server/bug_1.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_websocket_server: bug 1 +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + Co\run(function () use ($pm) { + global $count; + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + Assert::assert($cli->upgrade('/')); + $data1 = get_safe_random(random_int(1024, 8192)); + $cli->push($data1); + $frame1 = $cli->recv(); + Assert::eq($frame1->data, md5($data1)); + + $data2 = get_safe_random(random_int(65536, 65536 * 2)); + $pkt2 = Swoole\WebSocket\Server::pack($data2, WEBSOCKET_OPCODE_TEXT); + + $cli->socket->sendAll(substr($pkt2, 0, 4)); + usleep(1000); + $cli->socket->sendAll(substr($pkt2, 4)); + + $frame2 = $cli->recv(); + Assert::eq($frame2->data, md5($data2)); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'open_http2_protocol' => true, +// 'log_file' => '/dev/null', + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + $server->push($frame->fd, md5($frame->data)); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_websocket_server/close_frame_flag.phpt b/tests/swoole_websocket_server/close_frame_flag.phpt index 88bfe348438..01bb56b83f3 100644 --- a/tests/swoole_websocket_server/close_frame_flag.phpt +++ b/tests/swoole_websocket_server/close_frame_flag.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_websocket_server/close_frame_full.phpt b/tests/swoole_websocket_server/close_frame_full.phpt index 4d815e08114..c3f51509090 100644 --- a/tests/swoole_websocket_server/close_frame_full.phpt +++ b/tests/swoole_websocket_server/close_frame_full.phpt @@ -12,17 +12,16 @@ $pm->parentFunc = function (int $pid) use ($pm) { $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->set(['timeout' => 5]); for ($n = MAX_REQUESTS; $n--;) { - $ret = $cli->upgrade('/'); - Assert::assert($ret); + Assert::true($cli->upgrade('/')); $code = mt_rand(0, 5000); $reason = md5($code); - $close_frame = new swoole_websocket_closeframe; + $close_frame = new Swoole\WebSocket\CloseFrame; $close_frame->code = $code; $close_frame->reason = $reason; - $cli->push($close_frame); + Assert::true($cli->push($close_frame)); // recv the last close frame $frame = $cli->recv(); - Assert::isInstanceOf($frame, swoole_websocket_closeframe::class); + Assert::isInstanceOf($frame, Swoole\WebSocket\CloseFrame::class); Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE); Assert::same(md5($frame->code), $frame->reason); // connection closed @@ -32,11 +31,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -44,8 +43,8 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { - Assert::isInstanceOf($frame, swoole_websocket_closeframe::class); + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { + Assert::isInstanceOf($frame, Swoole\WebSocket\CloseFrame::class); Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE); if (mt_rand(0, 1)) { $serv->push($frame->fd, $frame); diff --git a/tests/swoole_websocket_server/disconnect.phpt b/tests/swoole_websocket_server/disconnect.phpt index 8761f83f9cb..92d92441635 100644 --- a/tests/swoole_websocket_server/disconnect.phpt +++ b/tests/swoole_websocket_server/disconnect.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -24,7 +24,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4000, 'shutdown received'); } diff --git a/tests/swoole_websocket_server/disconnect_with_code.phpt b/tests/swoole_websocket_server/disconnect_with_code.phpt index 7f3480cead4..e48333603b1 100644 --- a/tests/swoole_websocket_server/disconnect_with_code.phpt +++ b/tests/swoole_websocket_server/disconnect_with_code.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4001); } diff --git a/tests/swoole_websocket_server/dynamic_property.phpt b/tests/swoole_websocket_server/dynamic_property.phpt new file mode 100644 index 00000000000..0dacff7649b --- /dev/null +++ b/tests/swoole_websocket_server/dynamic_property.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_websocket_server: Creation of dynamic property is deprecated. +--SKIPIF-- + +--FILE-- +initFreePorts(10); +$websocket = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); +$port1 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port2 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port3 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port1->on('handshake', function($request, $response) {}); +$port1->on('beforehandshakeresponse', function($request, $response) {}); +var_dump($port1->getCallback('handshake') != null); +var_dump($port1->getCallback('BeforeHandshakeResponse') != null); + +$port2->on('HANDSHAKE', function($request, $response) {}); +$port2->on('BEFOREHANDSHAKERESPONSE', function($request, $response) {}); +var_dump($port1->getCallback('HANDSHAKE') != null); +var_dump($port1->getCallback('BEFOREHANDSHAKERESPONSE') != null); + +$port3->on('handShake', function($request, $response) {}); +$port3->on('beforehandShakeResponse', function($request, $response) {}); +var_dump($port1->getCallback('handShake') != null); +var_dump($port1->getCallback('beforehandShakeResponse') != null); +echo 'DONE'; +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +DONE diff --git a/tests/swoole_websocket_server/empty_message.phpt b/tests/swoole_websocket_server/empty_message.phpt index 7a76fcb13ff..7cc3fe7898c 100644 --- a/tests/swoole_websocket_server/empty_message.phpt +++ b/tests/swoole_websocket_server/empty_message.phpt @@ -21,11 +21,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { $json = json_decode($response->data); Assert::isEmpty($json->data); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -33,9 +33,9 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { $serv->push($frame->fd, json_encode($frame)); }); $serv->start(); diff --git a/tests/swoole_websocket_server/fin.phpt b/tests/swoole_websocket_server/fin.phpt index 024b9d2e606..3f0d8df307d 100644 --- a/tests/swoole_websocket_server/fin.phpt +++ b/tests/swoole_websocket_server/fin.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION; $finish = $n === 0; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $rand; $frame->finish = $finish; @@ -38,12 +38,12 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -51,7 +51,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { $serv->push($frame->fd, $frame); } else { diff --git a/tests/swoole_websocket_server/fin2.phpt b/tests/swoole_websocket_server/fin2.phpt index a45fae7a294..cd039535ffd 100644 --- a/tests/swoole_websocket_server/fin2.phpt +++ b/tests/swoole_websocket_server/fin2.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION; $finish = $n === 0; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $rand; $frame->finish = $finish; @@ -40,7 +40,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -48,7 +48,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { $frame->data = json_encode($frame); $serv->push($frame->fd, $frame); diff --git a/tests/swoole_websocket_server/get_large_requests.phpt b/tests/swoole_websocket_server/get_large_requests.phpt index 882b94b8069..a51a2716e8f 100644 --- a/tests/swoole_websocket_server/get_large_requests.phpt +++ b/tests/swoole_websocket_server/get_large_requests.phpt @@ -36,7 +36,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_websocket_server/get_small_requests.phpt b/tests/swoole_websocket_server/get_small_requests.phpt index aa49d256754..172d183965e 100644 --- a/tests/swoole_websocket_server/get_small_requests.phpt +++ b/tests/swoole_websocket_server/get_small_requests.phpt @@ -31,11 +31,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { co::sleep(0.001); // 50% block } diff --git a/tests/swoole_websocket_server/greeter.phpt b/tests/swoole_websocket_server/greeter.phpt index 1afcce54224..cc706504958 100644 --- a/tests/swoole_websocket_server/greeter.phpt +++ b/tests/swoole_websocket_server/greeter.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/'); Assert::assert($ret); - $data = sha1(get_safe_random(mt_rand(0, 1024))); + $data = sha1(get_safe_random(mt_rand(1, 1024))); for ($n = MAX_REQUESTS; $n--;) { $cli->push($data); $ret = $cli->recv(); @@ -26,12 +26,12 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS)); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { $server->push($frame->fd, "Hello {$frame->data}!"); $server->push($frame->fd, "How are you, {$frame->data}?"); }); diff --git a/tests/swoole_websocket_server/header_token.phpt b/tests/swoole_websocket_server/header_token.phpt new file mode 100644 index 00000000000..a5afa6a57ba --- /dev/null +++ b/tests/swoole_websocket_server/header_token.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_websocket_server: header token +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm, &$count) { + Co\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: websocket, abc\r\n" . + "Connection: Upgrade, abc\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: abc, websocket\r\n" . + "Connection: abc, Upgrade\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: abc, websocket, def\r\n" . + "Connection: abc, Upgrade, def\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set([ + 'log_file' => LOG_FILE, + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LOG_FILE); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_websocket_server/listener.phpt b/tests/swoole_websocket_server/listener.phpt index 85750a92a4d..8ffbd960a9a 100644 --- a/tests/swoole_websocket_server/listener.phpt +++ b/tests/swoole_websocket_server/listener.phpt @@ -21,10 +21,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_websocket_server/malformed_data.phpt b/tests/swoole_websocket_server/malformed_data.phpt new file mode 100644 index 00000000000..ee0ff7acd57 --- /dev/null +++ b/tests/swoole_websocket_server/malformed_data.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_websocket_server: malformed data +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm, &$count) { + $bytes = [chr(25)]; + swoole_loop_n(255, function () use (&$bytes) { + $bytes[] = chr(255); + }); + + Co\run(function () use ($bytes, $pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: websocket\r\n" . + "Connection: Upgrade\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $r1 = $cli->recv(); + Assert::contains($r1, 'HTTP/1.1 101 Switching Protocols'); + $cli->send(implode('', $bytes)); + $r2 = $cli->recv(); + Assert::eq($r2, false); + Assert::eq($cli->errCode, SOCKET_ECONNRESET); + }); + $pm->kill(); + $log = file_get_contents(LOG_FILE); + Assert::contains($log, 'malformed data'); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set([ + 'log_file' => LOG_FILE, + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LOG_FILE); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_websocket_server/message_size.phpt b/tests/swoole_websocket_server/message_size.phpt index 30bc116e8ab..b598c63b309 100644 --- a/tests/swoole_websocket_server/message_size.phpt +++ b/tests/swoole_websocket_server/message_size.phpt @@ -40,7 +40,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { test($cli, 70000, 400000); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; diff --git a/tests/swoole_websocket_server/onDisconnct.phpt b/tests/swoole_websocket_server/onDisconnct.phpt index c13642de141..8630460d876 100644 --- a/tests/swoole_websocket_server/onDisconnct.phpt +++ b/tests/swoole_websocket_server/onDisconnct.phpt @@ -8,6 +8,8 @@ require __DIR__ . '/../include/bootstrap.php'; use function Swoole\Coroutine\run; use Swoole\Coroutine\Http\Client; +use Swoole\WebSocket\Server; +use Swoole\WebSocket\Frame; $pm = new ProcessManager; @@ -22,11 +24,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { $client->push('hello world'); $client->close(); }); - puts('done!'); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -34,27 +35,28 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Server $serv, Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4000, 'shutdown received'); } }); $serv->on('connect', function ($s, $id) use ($pm) { - puts("connect ".$id); + puts("connect " . $id); }); $serv->on('disconnect', function ($s, $id) use ($pm) { - puts("disconnect ".$id); + puts("disconnect " . $id); }); $serv->on('open', function ($s, $req) use ($pm) { - puts("open ".$req->fd); + puts("open " . $req->fd); }); $serv->on('close', function ($s, $id) use ($pm) { - puts("close ".$id); + puts("close " . $id); }); $serv->start(); }; $pm->childFirst(); $pm->run(); +puts('done!'); ?> --EXPECT-- connect 1 diff --git a/tests/swoole_websocket_server/pack.phpt b/tests/swoole_websocket_server/pack.phpt index 339c03ddbd2..d8625e60dc1 100644 --- a/tests/swoole_websocket_server/pack.phpt +++ b/tests/swoole_websocket_server/pack.phpt @@ -4,14 +4,12 @@ swoole_websocket_server: websocket frame pack/unpack --FILE-- code = $code; $frame->reason = $data; } else { - $frame = new f; + $frame = new Frame; $frame->data = $data; } $frame->opcode = $opcode; @@ -43,14 +41,14 @@ for ($i = 1000; $i--;) { if (mt_rand(0, 1)) { $packed = (string)$frame; } else { - $packed = f::pack($frame); + $packed = Frame::pack($frame); } } else { - $packed = f::pack($data, $opcode, $finish); + $packed = Frame::pack($data, $opcode, $finish); } // unpack - $unpacked = f::unpack($packed); + $unpacked = Frame::unpack($packed); // verify if ($opcode === WEBSOCKET_OPCODE_CLOSE) { diff --git a/tests/swoole_websocket_server/pingloop.phpt b/tests/swoole_websocket_server/pingloop.phpt index d78f22252aa..fb20602e851 100644 --- a/tests/swoole_websocket_server/pingloop.phpt +++ b/tests/swoole_websocket_server/pingloop.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $count++; $loop++; if (mt_rand(0, 1)) { - $pong = new swoole_websocket_frame; + $pong = new Swoole\WebSocket\Frame; $pong->opcode = WEBSOCKET_OPCODE_PONG; $ret = $cli->push($pong); } else { @@ -42,23 +42,23 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($loop, PING_LOOP); }); } - swoole_event_wait(); + Swoole\Event::wait(); global $count; Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID); $pm->kill(); echo "DONE"; }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' ]); - $serv->on('workerStart', function (swoole_websocket_server $server) use ($pm) { - $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { + $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { + $timer_id = Swoole\Timer::tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $server->push($fd, $ping); } else { @@ -66,16 +66,16 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { - $server->clearTimer($timer_id); + Swoole\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { + Swoole\Timer::clear($timer_id); foreach ($server->connections as $fd) { - $server->push($fd, new swoole_websocket_closeframe); + $server->push($fd, new Swoole\WebSocket\CloseFrame); } }); $pm->wakeup(); }); $serv->on('open', function ($server, $req) { }); - $serv->on('message', function ($server, swoole_websocket_frame $frame) { + $serv->on('message', function ($server, Swoole\WebSocket\Frame $frame) { Assert::same($frame->opcode, WEBSOCKET_OPCODE_PONG); }); $serv->on('close', function ($server, $fd) { }); diff --git a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt index f8921a6ee73..2d62ad4960c 100644 --- a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt +++ b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $count++; $loop++; if (mt_rand(0, 1)) { - $pong = new swoole_websocket_frame; + $pong = new Swoole\WebSocket\Frame; $pong->data = 'pong'; $pong->opcode = WEBSOCKET_OPCODE_PONG; $ret = $cli->push($pong); @@ -44,25 +44,25 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($loop, PING_LOOP); }); } - swoole_event_wait(); + Swoole\Event::wait(); global $count; Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID); $pm->kill(); echo "DONE"; }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', 'open_websocket_ping_frame' => true, 'open_websocket_pong_frame' => true, ]); - $serv->on('workerStart', function (swoole_websocket_server $server) use ($pm) { - $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { + $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { + $timer_id = Swoole\Timer::tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->data = 'ping'; $ping->opcode = WEBSOCKET_OPCODE_PING; $server->push($fd, $ping); @@ -71,11 +71,11 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { - $server->clearTimer($timer_id); + Swoole\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { + Swoole\Timer::clear($timer_id); Swoole\Coroutine::sleep(0.1); // wait pong foreach ($server->connections as $fd) { - $server->push($fd, new swoole_websocket_closeframe); + $server->push($fd, new Swoole\WebSocket\CloseFrame); } }); $pm->wakeup(); @@ -83,7 +83,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('open', function ($server, $req) { }); $atomic = new Swoole\Atomic; - $serv->on('message', function ($server, swoole_websocket_frame $frame) use ($atomic) { + $serv->on('message', function ($server, Swoole\WebSocket\Frame $frame) use ($atomic) { if ($frame->opcode === WEBSOCKET_OPCODE_PONG) { Assert::same($frame->data, 'pong'); $atomic->add(); diff --git a/tests/swoole_websocket_server/pingpong.phpt b/tests/swoole_websocket_server/pingpong.phpt index 31bda865dc2..42a86b51a4b 100644 --- a/tests/swoole_websocket_server/pingpong.phpt +++ b/tests/swoole_websocket_server/pingpong.phpt @@ -12,9 +12,8 @@ $pm->parentFunc = function (int $pid) use ($pm) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/'); Assert::assert($ret); - for ($i = 100; $i--;) - { - $ping = new swoole_websocket_frame; + for ($i = 100; $i--;) { + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $ret = $cli->push($ping); Assert::assert($ret); @@ -23,10 +22,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt b/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt index 3a35d54dc4e..7aa4b157de0 100644 --- a/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt +++ b/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt @@ -13,7 +13,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $ret = $cli->upgrade('/'); Assert::assert($ret); for ($i = 100; $i--;) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $ping->data = 'ping'; $ret = $cli->push($ping); @@ -24,10 +24,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', @@ -40,11 +40,11 @@ $pm->childFunc = function () use ($pm) { $serv->on('open', function ($swoole_server, $req) { }); $atomic = new Swoole\Atomic; - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) use ($atomic) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) use ($atomic) { if ($frame->opcode === WEBSOCKET_OPCODE_PING) { Assert::same($frame->data, 'ping'); $atomic->add(); - $pongFrame = new swoole_websocket_frame; + $pongFrame = new Swoole\WebSocket\Frame; $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; $pongFrame->data = 'pong'; $server->push($frame->fd, $pongFrame); diff --git a/tests/swoole_websocket_server/query.phpt b/tests/swoole_websocket_server/query.phpt index 703a1602827..48a5664454f 100644 --- a/tests/swoole_websocket_server/query.phpt +++ b/tests/swoole_websocket_server/query.phpt @@ -21,11 +21,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($json['test'], 'a'); Assert::same($json['b'], 'hello'); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -33,10 +33,10 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { $serv->push($req->fd, json_encode($req->get)); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); diff --git a/tests/swoole_websocket_server/recv_decode.phpt b/tests/swoole_websocket_server/recv_decode.phpt index 03f9e6e872b..b1c4ad62513 100644 --- a/tests/swoole_websocket_server/recv_decode.phpt +++ b/tests/swoole_websocket_server/recv_decode.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { Assert::assert($ret); foreach ($data_list as $data) { if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = (int)explode('|', $data, 3)[1]; $frame->data = $data; $ret = $cli->push($frame); @@ -41,11 +41,11 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { Assert::assert(empty($data_list)); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $recv_frame) { + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $recv_frame) { global $data_list; list($id, $opcode) = explode('|', $recv_frame->data, 3); if (!Assert::assert($recv_frame->finish)) { @@ -68,7 +68,7 @@ $pm->childFunc = function () use ($pm) { return; } if (mt_rand(0, 1)) { - $send_frame = new swoole_websocket_frame; + $send_frame = new Swoole\WebSocket\Frame; $send_frame->data = $id; $serv->push($recv_frame->fd, $send_frame); } else { diff --git a/tests/swoole_websocket_server/send_encode.phpt b/tests/swoole_websocket_server/send_encode.phpt index 98784261d65..a98ca459bec 100644 --- a/tests/swoole_websocket_server/send_encode.phpt +++ b/tests/swoole_websocket_server/send_encode.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count, $data_list) { $ret = $cli->upgrade('/'); Assert::assert($ret); while (($frame = $cli->recv())) { - /**@var $frame swoole_websocket_frame */ + /**@var $frame Swoole\WebSocket\Frame */ list($id, $opcode) = explode('|', $frame->data, 3); Assert::assert($frame->finish); Assert::same($frame->opcode, (int)$opcode); @@ -41,13 +41,13 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count, $data_list) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, 0); echo "complete\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', @@ -57,12 +57,12 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { global $data_list; foreach ($data_list as $data) { $opcode = (int)explode('|', $data, 3)[1]; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $data; $ret = $serv->push($req->fd, $frame); @@ -74,7 +74,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { }); + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); }; $pm->childFirst(); diff --git a/tests/swoole_websocket_server/send_encode_async.phpt b/tests/swoole_websocket_server/send_encode_async.phpt index e832458047a..c26863f3b6e 100644 --- a/tests/swoole_websocket_server/send_encode_async.phpt +++ b/tests/swoole_websocket_server/send_encode_async.phpt @@ -43,12 +43,12 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { $cli->close(); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => TEST_LOG_FILE, @@ -58,12 +58,12 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { global $data_list; foreach ($data_list as $data) { $opcode = (int)explode('|', $data, 3)[1]; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $data; $ret = $serv->push($req->fd, $frame); @@ -75,7 +75,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { }); + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); }; $pm->childFirst(); diff --git a/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt new file mode 100644 index 00000000000..182aef947d9 --- /dev/null +++ b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_websocket_server: websocket server set cookie on beforeHandshakeResponse (#3270) +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Http\Client('127.0.0.1', $pm->getFreePort()); + if (Assert::true($cli->upgrade('/'))) { + Assert::same($cli->set_cookie_headers, [ + 'abc=def' + ]); + } + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('beforeHandShakeResponse', function (Server $server, Request $request, Response $response) { + $response->cookie('abc', 'def'); + }); + $server->on('message', function () { }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_websocket_server/set_cookie_on_hanshake.phpt b/tests/swoole_websocket_server/set_cookie_on_handshake.phpt similarity index 100% rename from tests/swoole_websocket_server/set_cookie_on_hanshake.phpt rename to tests/swoole_websocket_server/set_cookie_on_handshake.phpt diff --git a/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt b/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt new file mode 100644 index 00000000000..9fa24527b21 --- /dev/null +++ b/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt @@ -0,0 +1,93 @@ +--TEST-- +swoole_websocket_server: websocket compression with handshake +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Sec-WebSocket-Extensions' => 'permessage-deflate; client_max_window_bits']); + if (Assert::true($cli->upgrade('/'))) { + Assert::contains($cli->headers['sec-websocket-extensions'], 'permessage-deflate'); + } + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['websocket_compression' => true]); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + // test with Chrome + $server->on('request', function (Request $request, Response $response) use ($pm) { + $port = $pm->getFreePort(); + $response->end(<< +var wsServer = 'ws://127.0.0.1:{$port}'; +var websocket = new WebSocket(wsServer); +websocket.onopen = function (evt) { + console.log("Connected to WebSocket server."); +}; + +websocket.onclose = function (evt) { + console.log("Disconnected"); +}; + +websocket.onmessage = function (evt) { + console.log('Retrieved data from server: ' + evt.data); +}; + +websocket.onerror = function (evt, e) { + console.log('Error occured: ' + evt.data); +}; + +HTML); + }); + $server->on('handshake', function (Request $request, Response $response) { + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + $key = base64_encode(sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + )); + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + $response->status(101); + $response->end(); + return true; + }); + $server->on('message', function ($serv, $frame) { + $serv->push($frame->fd, "hello world"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/thirdparty/boost/asm/LICENSE b/thirdparty/boost/asm/LICENSE new file mode 100644 index 00000000000..36b7cd93cdf --- /dev/null +++ b/thirdparty/boost/asm/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index 35fd2098b8e..3aeb528fd97 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -1,22 +1,16 @@ #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - #if defined(__i386__) - #include "make_i386_sysv_elf_gas.S" - #include "jump_i386_sysv_elf_gas.S" - #elif defined(__x86_64__) + #if defined(__x86_64__) #include "make_x86_64_sysv_elf_gas.S" #include "jump_x86_64_sysv_elf_gas.S" - #elif defined(__ppc__) - #include "make_ppc32_sysv_elf_gas.S" - #include "jump_ppc32_sysv_elf_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_elf_gas.S" #include "jump_ppc64_sysv_elf_gas.S" - #elif defined(__arm__) - #include "make_arm_aapcs_elf_gas.S" - #include "jump_arm_aapcs_elf_gas.S" #elif defined(__arm64__) #include "make_arm64_aapcs_elf_gas.S" #include "jump_arm64_aapcs_elf_gas.S" + #elif defined(__loongarch64) + #include "make_loongarch64_sysv_elf_gas.S" + #include "jump_loongarch64_sysv_elf_gas.S" #else #error "No arch's" #endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 52821a9b9eb..47282c18e96 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,32 +51,21 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "jump_arm64_aapcs_elf_gas.S" .text .align 2 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: # prepare stack for GP + FPU sub sp, sp, #0xb0 -# Because gcc may save integer registers in fp registers across a -# function call we cannot skip saving the fp registers. -# -# Do not reinstate this test unless you fully understand what you -# are doing. -# -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 1f - # save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: # save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -88,17 +77,11 @@ jump_fcontext: # save LR as PC str x30, [sp, #0xa0] - # store RSP (pointing to context-data) in first argument (x0). - # STR cannot have sp as a target register + # store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - - # restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 2f + # restore RSP (pointing to context-data) from X1 + mov sp, x0 # load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -106,7 +89,6 @@ jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: # load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -115,9 +97,10 @@ jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - # use third arg as return value after jump - # and as first arg in context function - mov x0, x2 + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 # load pc ldr x4, [sp, #0xa0] @@ -126,8 +109,6 @@ jump_fcontext: add sp, sp, #0xb0 ret x4 -.size jump_fcontext,.-jump_fcontext -#ifndef __NetBSD__ +.size swoole_jump_fcontext,.-swoole_jump_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S index 958178ee719..dc544e026e4 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -46,26 +52,18 @@ *******************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .balign 16 -_jump_fcontext: +_swoole_jump_fcontext: ; prepare stack for GP + FPU sub sp, sp, #0xb0 -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 1f - ; save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: -#endif - ; save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -77,18 +75,11 @@ _jump_fcontext: ; save LR as PC str lr, [sp, #0xa0] - ; store RSP (pointing to context-data) in first argument (x0). - ; STR cannot have sp as a target register + ; store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - ; restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 2f + ; restore RSP (pointing to context-data) from X1 + mov sp, x0 ; load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -96,9 +87,6 @@ _jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: -#endif - ; load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -107,9 +95,10 @@ _jump_fcontext: ldp x27, x28, [sp, #0x80] ldp fp, lr, [sp, #0x90] - ; use third arg as return value after jump - ; and as first arg in context function - mov x0, x2 + ; return transfer_t from jump + ; pass transfer_t as first arg in context function + ; X0 == FCTX, X1 == DATA + mov x0, x4 ; load pc ldr x4, [sp, #0xa0] diff --git a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S deleted file mode 100644 index 7ad3c695ea7..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,%function -jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} -.size jump_fcontext,.-jump_fcontext - -#ifndef __NetBSD__ -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S deleted file mode 100644 index 75935347498..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ locate TLS to save/restore SjLj handler - mrc p15, 0, v2, c13, c0, #3 - bic v2, v2, #3 - - @ load TLS[__PTK_LIBC_DYLD_Unwind_SjLj_Key] - ldr v1, [v2, #72] - @ save SjLj handler - push {v1} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ restore SjLj handler - pop {v1} - @ store SjLj handler in TLS - str v1, [v2, #72] - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} diff --git a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm deleted file mode 100644 index 4e145dfa5bc..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,112 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - AREA |.text|, CODE - ALIGN 4 - EXPORT jump_fcontext - -jump_fcontext PROC - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #0x4c - - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: - ; load TIB to save/restore thread size and limit. - ; we do not need preserve CPU flag and can use it's arg register - mrc p15, #0, v1, c13, c0, #2 - - ; save current stack base - ldr a5, [v1,#0x04] - str a5, [sp,#0x48] - ; save current stack limit - ldr a5, [v1,#0x08] - str a5, [sp,#0x44] - ; save current deallocation stack - ldr a5, [v1,#0xe0c] - str a5, [sp,#0x40] - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: - ; restore stack base - ldr a5, [sp,#0x48] - str a5, [v1,#0x04] - ; restore stack limit - ldr a5, [sp,#0x44] - str a5, [v1,#0x08] - ; restore deallocation stack - ldr a5, [sp,#0x40] - str a5, [v1,#0xe0c] - - @ prepare stack for FPU - add sp, sp, #0x4c - - ; use third arg as return value after jump - ; and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR - pop {v1-v8,lr} - pop {pc} - - ENDP - END diff --git a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S index 34a32f785f2..773e8345dfe 100644 --- a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S @@ -7,16 +7,10 @@ // Stub file for universal binary -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +#if defined(__x86_64__) #include "jump_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "jump_ppc64_sysv_macho_gas.S" -#elif defined(__arm__) - #include "jump_arm_aapcs_macho_gas.S" #elif defined(__arm64__) #include "jump_arm64_aapcs_macho_gas.S" #else diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm b/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm deleted file mode 100644 index b55b77207a4..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -* *****************************************************************/ - -.file "jump_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _jump_fcontext -.def _jump_fcontext; .scl 2; .type 32; .endef -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* load NT_TIB */ - movl %fs:(0x18), %edx - - /* load current SEH exception list */ - movl (%edx), %eax - push %eax - - /* load current stack base */ - movl 0x04(%edx), %eax - push %eax - - /* load current stack limit */ - movl 0x08(%edx), %eax - push %eax - - /* load current dealloction stack */ - movl 0xe0c(%edx), %eax - push %eax - - /* load fiber local storage */ - movl 0x10(%edx), %eax - push %eax - - /* prepare stack for FPU */ - leal -0x08(%esp), %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 1f - - /* save MMX control word */ - stmxcsr (%esp) - /* save x87 control word */ - fnstcw 0x04(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x30(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x34(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x38(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x04(%esp) - -2: - /* prepare stack for FPU */ - leal 0x08(%esp), %esp - - /* load NT_TIB into ECX */ - movl %fs:(0x18), %edx - - /* restore fiber local storage */ - popl %ecx - movl %ecx, 0x10(%edx) - - /* restore current deallocation stack */ - popl %ecx - movl %ecx, 0xe0c(%edx) - - /* restore current stack limit */ - popl %ecx - movl %ecx, 0x08(%edx) - - /* restore current stack base */ - popl %ecx - movl %ecx, 0x04(%edx) - - /* restore current SEH exception list */ - popl %ecx - movl %ecx, (%edx) - - popl %edi /* save EDI */ - popl %esi /* save ESI */ - popl %ebx /* save EBX */ - popl %ebp /* save EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x04(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm b/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm deleted file mode 100644 index d12f7109055..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm +++ /dev/null @@ -1,142 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT - ; fourth arg of jump_fcontext() == flag indicating preserving FPU - mov ecx, [esp+010h] - - push ebp ; save EBP - push ebx ; save EBX - push esi ; save ESI - push edi ; save EDI - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; load current SEH exception list - mov eax, [edx] - push eax - - ; load current stack base - mov eax, [edx+04h] - push eax - - ; load current stack limit - mov eax, [edx+08h] - push eax - - ; load current deallocation stack - mov eax, [edx+0e0ch] - push eax - - ; load fiber local storage - mov eax, [edx+010h] - push eax - - ; prepare stack for FPU - lea esp, [esp-08h] - - ; test for flag preserve_fpu - test ecx, ecx - je nxt1 - - ; save MMX control- and status-word - stmxcsr [esp] - ; save x87 control-word - fnstcw [esp+04h] - -nxt1: - ; first arg of jump_fcontext() == context jumping from - mov eax, [esp+030h] - - ; store ESP (pointing to context-data) in EAX - mov [eax], esp - - ; second arg of jump_fcontext() == context jumping to - mov edx, [esp+034h] - - ; third arg of jump_fcontext() == value to be returned after jump - mov eax, [esp+038h] - - ; restore ESP (pointing to context-data) from EDX - mov esp, edx - - ; test for flag preserve_fpu - test ecx, ecx - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [esp] - ; restore x87 control-word - fldcw [esp+04h] - -nxt2: - ; prepare stack for FPU - lea esp, [esp+08h] - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; restore fiber local storage - pop ecx - mov [edx+010h], ecx - - ; restore current deallocation stack - pop ecx - mov [edx+0e0ch], ecx - - ; restore current stack limit - pop ecx - mov [edx+08h], ecx - - ; restore current stack base - pop ecx - mov [edx+04h], ecx - - ; restore current SEH exception list - pop ecx - mov [edx], ecx - - pop edi ; save EDI - pop esi ; save ESI - pop ebx ; save EBX - pop ebp ; save EBP - - ; restore return-address - pop edx - - ; use value in EAX as return-value after jump - ; use value in EAX as first arg in context function - mov [esp+04h], eax - - ; indirect jump to context - jmp edx -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S b/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S deleted file mode 100644 index aa47b0f3967..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx -.size jump_fcontext,.-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S deleted file mode 100644 index 05d669f56ae..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index 959ddac16f8..00000000000 --- a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "jump_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..89a08821ca6 --- /dev/null +++ b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S @@ -0,0 +1,121 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 2 +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: + # reserve space on stack + addi.d $sp, $sp, -160 + + # save fs0 - fs7 + fst.d $fs0, $sp, 0 + fst.d $fs1, $sp, 8 + fst.d $fs2, $sp, 16 + fst.d $fs3, $sp, 24 + fst.d $fs4, $sp, 32 + fst.d $fs5, $sp, 40 + fst.d $fs6, $sp, 48 + fst.d $fs7, $sp, 56 + + # save s0 - s8, fp, ra + st.d $s0, $sp, 64 + st.d $s1, $sp, 72 + st.d $s2, $sp, 80 + st.d $s3, $sp, 88 + st.d $s4, $sp, 96 + st.d $s5, $sp, 104 + st.d $s6, $sp, 112 + st.d $s7, $sp, 120 + st.d $s8, $sp, 128 + st.d $fp, $sp, 136 + st.d $ra, $sp, 144 + + # save RA as PC + st.d $ra, $sp, 152 + + # store SP (pointing to context-data) in A2 + move $a2, $sp + + # restore SP (pointing to context-data) from A0 + move $sp, $a0 + + # load fs0 - fs7 + fld.d $fs0, $sp, 0 + fld.d $fs1, $sp, 8 + fld.d $fs2, $sp, 16 + fld.d $fs3, $sp, 24 + fld.d $fs4, $sp, 32 + fld.d $fs5, $sp, 40 + fld.d $fs6, $sp, 48 + fld.d $fs7, $sp, 56 + + #load s0 - s7 + ld.d $s0, $sp, 64 + ld.d $s1, $sp, 72 + ld.d $s2, $sp, 80 + ld.d $s3, $sp, 88 + ld.d $s4, $sp, 96 + ld.d $s5, $sp, 104 + ld.d $s6, $sp, 112 + ld.d $s7, $sp, 120 + ld.d $s8, $sp, 128 + ld.d $fp, $sp, 136 + ld.d $ra, $sp, 144 + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + move $a0, $a2 + + # load PC + ld.d $a2, $sp, 152 + + # restore stack + addi.d $sp, $sp, 160 + + # jump to context + jr $a2 +.size swoole_jump_fcontext, .-swoole_jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S b/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S deleted file mode 100644 index 67cc0796a6d..00000000000 --- a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: - # reserve space on stack - addiu $sp, $sp, -92 - - sw $s0, 48($sp) # save S0 - sw $s1, 52($sp) # save S1 - sw $s2, 56($sp) # save S2 - sw $s3, 60($sp) # save S3 - sw $s4, 64($sp) # save S4 - sw $s5, 68($sp) # save S5 - sw $s6, 72($sp) # save S6 - sw $s7, 76($sp) # save S7 - sw $fp, 80($sp) # save FP - sw $ra, 84($sp) # save RA - sw $ra, 88($sp) # save RA as PC - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - - s.d $f20, ($sp) # save F20 - s.d $f22, 8($sp) # save F22 - s.d $f24, 16($sp) # save F24 - s.d $f26, 24($sp) # save F26 - s.d $f28, 32($sp) # save F28 - s.d $f30, 40($sp) # save F30 - -1: -#endif - - # store SP (pointing to context-data) in A0 - sw $sp, ($a0) - - # restore SP (pointing to context-data) from A1 - move $sp, $a1 - - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f20, ($sp) # restore F20 - l.d $f22, 8($sp) # restore F22 - l.d $f24, 16($sp) # restore F24 - l.d $f26, 24($sp) # restore F26 - l.d $f28, 32($sp) # restore F28 - l.d $f30, 40($sp) # restore F30 - -2: -#endif - - lw $s0, 48($sp) # restore S0 - lw $s1, 52($sp) # restore S1 - lw $s2, 56($sp) # restore S2 - lw $s3, 60($sp) # restore S3 - lw $s4, 64($sp) # restore S4 - lw $s5, 68($sp) # restore S5 - lw $s6, 72($sp) # restore S6 - lw $s7, 76($sp) # restore S7 - lw $fp, 80($sp) # restore FP - lw $ra, 84($sp) # restore RA - - # load PC - lw $t9, 88($sp) - - # adjust stack - addiu $sp, $sp, 92 - - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 - - # jump to context - jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S index ba2b230dbcb..edff6ec0501 100644 --- a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -5,10 +5,6 @@ http://www.boost.org/LICENSE_1_0.txt) */ -/* - "backported" version of original jump_mips64_n64_elf_gas.S -*/ - /******************************************************* * * * ------------------------------------------------- * @@ -49,12 +45,13 @@ * * * *****************************************************/ +.file "jump_mips64_n64_elf_gas.S" .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 3 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: +.type swoole_jump_fcontext,@function +.ent swoole_jump_fcontext +swoole_jump_fcontext: # reserve space on stack daddiu $sp, $sp, -160 @@ -71,9 +68,6 @@ jump_fcontext: sd $ra, 152($sp) # save RA as PC #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - s.d $f24, 0($sp) # save F24 s.d $f25, 8($sp) # save F25 s.d $f26, 16($sp) # save F26 @@ -82,20 +76,15 @@ jump_fcontext: s.d $f29, 40($sp) # save F29 s.d $f30, 48($sp) # save F30 s.d $f31, 56($sp) # save F31 -1: #endif - # store SP (pointing to context-data) in A0 - sd $sp, ($a0) + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp - # restore SP (pointing to context-data) from A1 - move $sp, $a1 + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f24, 0($sp) # restore F24 l.d $f25, 8($sp) # restore F25 l.d $f26, 16($sp) # restore F26 @@ -104,7 +93,6 @@ jump_fcontext: l.d $f29, 40($sp) # restore F29 l.d $f30, 48($sp) # restore F30 l.d $f31, 56($sp) # restore F31 -2: #endif ld $s0, 64($sp) # restore S0 @@ -124,16 +112,13 @@ jump_fcontext: # adjust stack daddiu $sp, $sp, 160 - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 + move $a0, $v0 # move old sp from v0 to a0 as param + move $v1, $a1 # move *data from a1 to v1 as return # jump to context jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext +.end swoole_jump_fcontext +.size swoole_jump_fcontext, .-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits - diff --git a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index f175e31233f..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "jump_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S deleted file mode 100644 index f3c799237c6..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,210 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -jump_fcontext: - # reserve space on stack - subi %r1, %r1, 240 - - stw %r13, 152(%r1) # save R13 - stw %r14, 156(%r1) # save R14 - stw %r15, 160(%r1) # save R15 - stw %r16, 164(%r1) # save R16 - stw %r17, 168(%r1) # save R17 - stw %r18, 172(%r1) # save R18 - stw %r19, 176(%r1) # save R19 - stw %r20, 180(%r1) # save R20 - stw %r21, 184(%r1) # save R21 - stw %r22, 188(%r1) # save R22 - stw %r23, 192(%r1) # save R23 - stw %r24, 196(%r1) # save R24 - stw %r25, 200(%r1) # save R25 - stw %r26, 204(%r1) # save R26 - stw %r27, 208(%r1) # save R27 - stw %r28, 212(%r1) # save R28 - stw %r29, 216(%r1) # save R29 - stw %r30, 220(%r1) # save R30 - stw %r31, 224(%r1) # save R31 - - # save CR - mfcr %r0 - stw %r0, 228(%r1) - # save LR - mflr %r0 - stw %r0, 232(%r1) - # save LR as PC - stw %r0, 236(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR - -1: - # store RSP (pointing to context-data) in R3 - stw %r1, 0(%r3) - - # restore RSP (pointing to context-data) from R4 - mr %r1, %r4 - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: - lwz %r13, 152(%r1) # restore R13 - lwz %r14, 156(%r1) # restore R14 - lwz %r15, 160(%r1) # restore R15 - lwz %r16, 164(%r1) # restore R16 - lwz %r17, 168(%r1) # restore R17 - lwz %r18, 172(%r1) # restore R18 - lwz %r19, 176(%r1) # restore R19 - lwz %r20, 180(%r1) # restore R20 - lwz %r21, 184(%r1) # restore R21 - lwz %r22, 188(%r1) # restore R22 - lwz %r23, 192(%r1) # restore R23 - lwz %r24, 196(%r1) # restore R24 - lwz %r25, 200(%r1) # restore R25 - lwz %r26, 204(%r1) # restore R26 - lwz %r27, 208(%r1) # restore R27 - lwz %r28, 212(%r1) # restore R28 - lwz %r29, 216(%r1) # restore R29 - lwz %r30, 220(%r1) # restore R30 - lwz %r31, 224(%r1) # restore R31 - - # restore CR - lwz %r0, 228(%r1) - mtcr %r0 - # restore LR - lwz %r0, 232(%r1) - mtlr %r0 - - # load PC - lwz %r0, 236(%r1) - # restore CTR - mtctr %r0 - - # adjust stack - addi %r1, %r1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 - - # jump to context - bctr -.size jump_fcontext, .-jump_fcontext - -#ifndef __NetBSD__ -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S deleted file mode 100644 index 59d8f49746b..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,203 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - ; reserve space on stack - subi r1, r1, 240 - - stw r13, 152(r1) ; save R13 - stw r14, 156(r1) ; save R14 - stw r15, 160(r1) ; save R15 - stw r16, 164(r1) ; save R16 - stw r17, 168(r1) ; save R17 - stw r18, 172(r1) ; save R18 - stw r19, 176(r1) ; save R19 - stw r20, 180(r1) ; save R20 - stw r21, 184(r1) ; save R21 - stw r22, 188(r1) ; save R22 - stw r23, 192(r1) ; save R23 - stw r24, 196(r1) ; save R24 - stw r25, 200(r1) ; save R25 - stw r26, 204(r1) ; save R26 - stw r27, 208(r1) ; save R27 - stw r28, 212(r1) ; save R28 - stw r29, 216(r1) ; save R29 - stw r30, 220(r1) ; save R30 - stw r31, 224(r1) ; save R31 - - ; save CR - mfcr r0 - stw r0, 228(r1) - ; save LR - mflr r0 - stw r0, 232(r1) - ; save LR as PC - stw r0, 236(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) - - ; restore RSP (pointing to context-data) from R4 - mr r1, r4 - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -l2: - lwz r13, 152(r1) ; restore R13 - lwz r14, 156(r1) ; restore R14 - lwz r15, 160(r1) ; restore R15 - lwz r16, 164(r1) ; restore R16 - lwz r17, 168(r1) ; restore R17 - lwz r18, 172(r1) ; restore R18 - lwz r19, 176(r1) ; restore R19 - lwz r20, 180(r1) ; restore R20 - lwz r21, 184(r1) ; restore R21 - lwz r22, 188(r1) ; restore R22 - lwz r23, 192(r1) ; restore R23 - lwz r24, 196(r1) ; restore R24 - lwz r25, 200(r1) ; restore R25 - lwz r26, 204(r1) ; restore R26 - lwz r27, 208(r1) ; restore R27 - lwz r28, 212(r1) ; restore R28 - lwz r29, 216(r1) ; restore R29 - lwz r30, 220(r1) ; restore R30 - lwz r31, 224(r1) ; restore R31 - - ; restore CR - lwz r0, 228(r1) - mtcr r0 - ; restore LR - lwz r0, 232(r1) - mtlr r0 - - ; load PC - lwz r0, 236(r1) - ; restore CTR - mtctr r0 - - ; adjust stack - addi r1, r1, 240 - - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 - - ; jump to context - bctr diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index 7cdf8a92883..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,138 +0,0 @@ -.globl .jump_fcontext -.globl jump_fcontext[DS] -.align 2 -.csect jump_fcontext[DS] -jump_fcontext: - .long .jump_fcontext -.jump_fcontext: - # reserve space on stack - subi 1, 1, 240 - - stw 13, 152(1) # save R13 - stw 14, 156(1) # save R14 - stw 15, 160(1) # save R15 - stw 16, 164(1) # save R16 - stw 17, 168(1) # save R17 - stw 18, 172(1) # save R18 - stw 19, 176(1) # save R19 - stw 20, 180(1) # save R20 - stw 21, 184(1) # save R21 - stw 22, 188(1) # save R22 - stw 23, 192(1) # save R23 - stw 24, 196(1) # save R24 - stw 25, 200(1) # save R25 - stw 26, 204(1) # save R26 - stw 27, 208(1) # save R27 - stw 28, 212(1) # save R28 - stw 29, 216(1) # save R29 - stw 30, 220(1) # save R30 - stw 31, 224(1) # save R31 - - # save CR - mfcr 0 - stw 0, 228(1) - # save LR - mflr 0 - stw 0, 232(1) - # save LR as PC - stw 0, 236(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) - - # restore RSP (pointing to context-data) from R4 - mr 1, 4 - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - lwz 13, 152(1) # restore R13 - lwz 14, 156(1) # restore R14 - lwz 15, 160(1) # restore R15 - lwz 16, 164(1) # restore R16 - lwz 17, 168(1) # restore R17 - lwz 18, 172(1) # restore R18 - lwz 19, 176(1) # restore R19 - lwz 20, 180(1) # restore R20 - lwz 21, 184(1) # restore R21 - lwz 22, 188(1) # restore R22 - lwz 23, 192(1) # restore R23 - lwz 24, 196(1) # restore R24 - lwz 25, 200(1) # restore R25 - lwz 26, 204(1) # restore R26 - lwz 27, 208(1) # restore R27 - lwz 28, 212(1) # restore R28 - lwz 29, 216(1) # restore R29 - lwz 30, 220(1) # restore R30 - lwz 31, 224(1) # restore R31 - - # restore CR - lwz 0, 228(1) - mtcr 0 - # restore LR - lwz 0, 232(1) - mtlr 0 - - # load PC - lwz 0, 236(1) - # restore CTR - mtctr 0 - - # adjust stack - addi 1, 1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 - - # jump to context - bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S index bd7bc9f6779..a90ffbe1681 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -12,258 +12,210 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ -.globl jump_fcontext +.file "jump_ppc64_sysv_elf_gas.S" +.globl swoole_jump_fcontext #if _CALL_ELF == 2 .text .align 2 -jump_fcontext: - addis %r2, %r12, .TOC.-jump_fcontext@ha - addi %r2, %r2, .TOC.-jump_fcontext@l - .localentry jump_fcontext, . - jump_fcontext +swoole_jump_fcontext: + addis %r2, %r12, .TOC.-swoole_jump_fcontext@ha + addi %r2, %r2, .TOC.-swoole_jump_fcontext@l + .localentry swoole_jump_fcontext, . - swoole_jump_fcontext #else .section ".opd","aw" .align 3 -jump_fcontext: +swoole_jump_fcontext: # ifdef _CALL_LINUX - .quad .L.jump_fcontext,.TOC.@tocbase,0 - .type jump_fcontext,@function + .quad .L.swoole_jump_fcontext,.TOC.@tocbase,0 + .type swoole_jump_fcontext,@function .text .align 2 -.L.jump_fcontext: +.L.swoole_jump_fcontext: # else - .hidden .jump_fcontext - .globl .jump_fcontext - .quad .jump_fcontext,.TOC.@tocbase,0 - .size jump_fcontext,24 - .type .jump_fcontext,@function + .hidden .swoole_jump_fcontext + .globl .swoole_jump_fcontext + .quad .swoole_jump_fcontext,.TOC.@tocbase,0 + .size swoole_jump_fcontext,24 + .type .swoole_jump_fcontext,@function .text .align 2 -.jump_fcontext: +.swoole_jump_fcontext: # endif #endif # reserve space on stack - subi %r1, %r1, 328 + subi %r1, %r1, 184 #if _CALL_ELF != 2 - std %r2, 152(%r1) # save TOC + std %r2, 0(%r1) # save TOC +#endif + std %r14, 8(%r1) # save R14 + std %r15, 16(%r1) # save R15 + std %r16, 24(%r1) # save R16 + std %r17, 32(%r1) # save R17 + std %r18, 40(%r1) # save R18 + std %r19, 48(%r1) # save R19 + std %r20, 56(%r1) # save R20 + std %r21, 64(%r1) # save R21 + std %r22, 72(%r1) # save R22 + std %r23, 80(%r1) # save R23 + std %r24, 88(%r1) # save R24 + std %r25, 96(%r1) # save R25 + std %r26, 104(%r1) # save R26 + std %r27, 112(%r1) # save R27 + std %r28, 120(%r1) # save R28 + std %r29, 128(%r1) # save R29 + std %r30, 136(%r1) # save R30 + std %r31, 144(%r1) # save R31 +#if _CALL_ELF != 2 + std %r3, 152(%r1) # save hidden #endif - std %r14, 160(%r1) # save R14 - std %r15, 168(%r1) # save R15 - std %r16, 176(%r1) # save R16 - std %r17, 184(%r1) # save R17 - std %r18, 192(%r1) # save R18 - std %r19, 200(%r1) # save R19 - std %r20, 208(%r1) # save R20 - std %r21, 216(%r1) # save R21 - std %r22, 224(%r1) # save R22 - std %r23, 232(%r1) # save R23 - std %r24, 240(%r1) # save R24 - std %r25, 248(%r1) # save R25 - std %r26, 256(%r1) # save R26 - std %r27, 264(%r1) # save R27 - std %r28, 272(%r1) # save R28 - std %r29, 280(%r1) # save R29 - std %r30, 288(%r1) # save R30 - std %r31, 296(%r1) # save R31 # save CR mfcr %r0 - std %r0, 304(%r1) + std %r0, 160(%r1) # save LR mflr %r0 - std %r0, 312(%r1) + std %r0, 168(%r1) # save LR as PC - std %r0, 320(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR + std %r0, 176(%r1) -1: - # store RSP (pointing to context-data) in R3 - std %r1, 0(%r3) + # store RSP (pointing to context-data) in R6 + mr %r6, %r1 +#if _CALL_ELF == 2 + # restore RSP (pointing to context-data) from R3 + mr %r1, %r3 +#else # restore RSP (pointing to context-data) from R4 mr %r1, %r4 - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: + ld %r2, 0(%r1) # restore TOC +#endif + ld %r14, 8(%r1) # restore R14 + ld %r15, 16(%r1) # restore R15 + ld %r16, 24(%r1) # restore R16 + ld %r17, 32(%r1) # restore R17 + ld %r18, 40(%r1) # restore R18 + ld %r19, 48(%r1) # restore R19 + ld %r20, 56(%r1) # restore R20 + ld %r21, 64(%r1) # restore R21 + ld %r22, 72(%r1) # restore R22 + ld %r23, 80(%r1) # restore R23 + ld %r24, 88(%r1) # restore R24 + ld %r25, 96(%r1) # restore R25 + ld %r26, 104(%r1) # restore R26 + ld %r27, 112(%r1) # restore R27 + ld %r28, 120(%r1) # restore R28 + ld %r29, 128(%r1) # restore R29 + ld %r30, 136(%r1) # restore R30 + ld %r31, 144(%r1) # restore R31 #if _CALL_ELF != 2 - ld %r2, 152(%r1) # restore TOC + ld %r3, 152(%r1) # restore hidden #endif - ld %r14, 160(%r1) # restore R14 - ld %r15, 168(%r1) # restore R15 - ld %r16, 176(%r1) # restore R16 - ld %r17, 184(%r1) # restore R17 - ld %r18, 192(%r1) # restore R18 - ld %r19, 200(%r1) # restore R19 - ld %r20, 208(%r1) # restore R20 - ld %r21, 216(%r1) # restore R21 - ld %r22, 224(%r1) # restore R22 - ld %r23, 232(%r1) # restore R23 - ld %r24, 240(%r1) # restore R24 - ld %r25, 248(%r1) # restore R25 - ld %r26, 256(%r1) # restore R26 - ld %r27, 264(%r1) # restore R27 - ld %r28, 272(%r1) # restore R28 - ld %r29, 280(%r1) # restore R29 - ld %r30, 288(%r1) # restore R30 - ld %r31, 296(%r1) # restore R31 # restore CR - ld %r0, 304(%r1) + ld %r0, 160(%r1) mtcr %r0 # restore LR - ld %r0, 312(%r1) + ld %r0, 168(%r1) mtlr %r0 # load PC - ld %r12, 320(%r1) + ld %r12, 176(%r1) # restore CTR mtctr %r12 # adjust stack - addi %r1, %r1, 328 + addi %r1, %r1, 184 - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 +#if _CALL_ELF == 2 + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + # arg pointer already in %r4 # jump to context bctr -#if _CALL_ELF == 2 - .size jump_fcontext, .-jump_fcontext + .size swoole_jump_fcontext, .-swoole_jump_fcontext #else + # zero in r3 indicates first jump to context-function + cmpdi %r3, 0 + beq use_entry_arg + + # return transfer_t + std %r6, 0(%r3) + std %r5, 8(%r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + mr %r4, %r5 + + # jump to context + bctr # ifdef _CALL_LINUX - .size .jump_fcontext, .-.L.jump_fcontext + .size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext # else - .size .jump_fcontext, .-.jump_fcontext + .size .swoole_jump_fcontext, .-.swoole_jump_fcontext # endif #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S index d94b7f65a0d..abea7940628 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S @@ -12,215 +12,153 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text .align 2 -.globl jump_fcontext +.globl _swoole_jump_fcontext -_jump_fcontext: +_swoole_jump_fcontext: ; reserve space on stack - subi r1, r1, 328 - - std r13, 152(r1) ; save R13 - std r14, 160(r1) ; save R14 - std r15, 168(r1) ; save R15 - std r16, 176(r1) ; save R16 - std r17, 184(r1) ; save R17 - std r18, 192(r1) ; save R18 - std r19, 200(r1) ; save R19 - std r20, 208(r1) ; save R20 - std r21, 216(r1) ; save R21 - std r22, 224(r1) ; save R22 - std r23, 232(r1) ; save R23 - std r24, 240(r1) ; save R24 - std r25, 248(r1) ; save R25 - std r26, 256(r1) ; save R26 - std r27, 264(r1) ; save R27 - std r28, 272(r1) ; save R28 - std r29, 280(r1) ; save R29 - std r30, 288(r1) ; save R30 - std r31, 296(r1) ; save R31 + subi r1, r1, 184 + + std r14, 8(r1) ; save R14 + std r15, 16(r1) ; save R15 + std r16, 24(r1) ; save R16 + std r17, 32(r1) ; save R17 + std r18, 40(r1) ; save R18 + std r19, 48(r1) ; save R19 + std r20, 56(r1) ; save R20 + std r21, 64(r1) ; save R21 + std r22, 72(r1) ; save R22 + std r23, 80(r1) ; save R23 + std r24, 88(r1) ; save R24 + std r25, 96(r1) ; save R25 + std r26, 104(r1) ; save R26 + std r27, 112(r1) ; save R27 + std r28, 120(r1) ; save R28 + std r29, 128(r1) ; save R29 + std r30, 136(r1) ; save R30 + std r31, 144(r1) ; save R31 + std r3, 152(r1) ; save hidden ; save CR mfcr r0 - std r0, 304(r1) + std r0, 160(r1) ; save LR mflr r0 - std r0, 312(r1) + std r0, 168(r1) ; save LR as PC - std r0, 320(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) + std r0, 176(r1) + + ; store RSP (pointing to context-data) in R6 + mr r6, r1 ; restore RSP (pointing to context-data) from R4 mr r1, r4 - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -2: - ld r13, 152(r1) ; restore R13 - ld r14, 160(r1) ; restore R14 - ld r15, 168(r1) ; restore R15 - ld r16, 176(r1) ; restore R16 - ld r17, 184(r1) ; restore R17 - ld r18, 192(r1) ; restore R18 - ld r19, 200(r1) ; restore R19 - ld r20, 208(r1) ; restore R20 - ld r21, 216(r1) ; restore R21 - ld r22, 224(r1) ; restore R22 - ld r23, 232(r1) ; restore R23 - ld r24, 240(r1) ; restore R24 - ld r25, 248(r1) ; restore R25 - ld r26, 256(r1) ; restore R26 - ld r27, 264(r1) ; restore R27 - ld r28, 272(r1) ; restore R28 - ld r29, 280(r1) ; restore R29 - ld r30, 288(r1) ; restore R30 - ld r31, 296(r1) ; restore R31 + ld r14, 8(r1) ; restore R14 + ld r15, 16(r1) ; restore R15 + ld r16, 24(r1) ; restore R16 + ld r17, 32(r1) ; restore R17 + ld r18, 40(r1) ; restore R18 + ld r19, 48(r1) ; restore R19 + ld r20, 56(r1) ; restore R20 + ld r21, 64(r1) ; restore R21 + ld r22, 72(r1) ; restore R22 + ld r23, 80(r1) ; restore R23 + ld r24, 88(r1) ; restore R24 + ld r25, 96(r1) ; restore R25 + ld r26, 104(r1) ; restore R26 + ld r27, 112(r1) ; restore R27 + ld r28, 120(r1) ; restore R28 + ld r29, 128(r1) ; restore R29 + ld r30, 136(r1) ; restore R30 + ld r31, 144(r1) ; restore R31 + ld r3, 152(r1) ; restore hidden ; restore CR - ld r0, 304(r1) + ld r0, 160(r1) mtcr r0 ; restore LR - ld r0, 312(r1) + ld r0, 168(r1) mtlr r0 ; load PC - ld r0, 320(r1) + ld r12, 176(r1) ; restore CTR - mtctr r0 + mtctr r12 ; adjust stack - addi r1, r1, 328 + addi r1, r1, 184 + + ; zero in r3 indicates first jump to context-function + cmpdi r3, 0 + beq use_entry_arg + + ; return transfer_t + std r6, 0(r3) + std r5, 8(r3) + + ; jump to context + bctr - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 +use_entry_arg: + ; copy transfer_t into transfer_fn arg registers + mr r3, r6 + mr r4, r5 ; jump to context bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S index 65d60d4f54b..a125f681b5e 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S @@ -1,134 +1,173 @@ -.align 2 -.globl .jump_fcontext -.jump_fcontext: + +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "jump_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_jump_fcontext[DS] + .globl .swoole_jump_fcontext + .csect swoole_jump_fcontext[DS], 3 +swoole_jump_fcontext: + .llong .swoole_jump_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_jump_fcontext: # reserve space on stack - subi 1, 1, 328 - - std 13, 152(1) # save R13 - std 14, 160(1) # save R14 - std 15, 168(1) # save R15 - std 16, 176(1) # save R16 - std 17, 184(1) # save R17 - std 18, 192(1) # save R18 - std 19, 200(1) # save R19 - std 20, 208(1) # save R20 - std 21, 216(1) # save R21 - std 22, 224(1) # save R22 - std 23, 232(1) # save R23 - std 24, 240(1) # save R24 - std 25, 248(1) # save R25 - std 26, 256(1) # save R26 - std 27, 264(1) # save R27 - std 28, 272(1) # save R28 - std 29, 280(1) # save R29 - std 30, 288(1) # save R30 - std 31, 296(1) # save R31 + subi 1, 1, 184 + + std 2, 0(1) # save TOC + std 14, 8(1) # save R14 + std 15, 16(1) # save R15 + std 16, 24(1) # save R16 + std 17, 32(1) # save R17 + std 18, 40(1) # save R18 + std 19, 48(1) # save R19 + std 20, 56(1) # save R20 + std 21, 64(1) # save R21 + std 22, 72(1) # save R22 + std 23, 80(1) # save R23 + std 24, 88(1) # save R24 + std 25, 96(1) # save R25 + std 26, 104(1) # save R26 + std 27, 112(1) # save R27 + std 28, 120(1) # save R28 + std 29, 128(1) # save R29 + std 30, 136(1) # save R30 + std 31, 144(1) # save R31 + std 3, 152(1) # save hidden # save CR mfcr 0 - std 0, 304(1) + std 0, 160(1) # save LR mflr 0 - std 0, 312(1) + std 0, 168(1) # save LR as PC - std 0, 320(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) + std 0, 176(1) + + # store RSP (pointing to context-data) in R6 + mr 6, 1 # restore RSP (pointing to context-data) from R4 mr 1, 4 - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - ld 13, 152(1) # restore R13 - ld 14, 160(1) # restore R14 - ld 15, 168(1) # restore R15 - ld 16, 176(1) # restore R16 - ld 17, 184(1) # restore R17 - ld 18, 192(1) # restore R18 - ld 19, 200(1) # restore R19 - ld 20, 208(1) # restore R20 - ld 21, 216(1) # restore R21 - ld 22, 224(1) # restore R22 - ld 23, 232(1) # restore R23 - ld 24, 240(1) # restore R24 - ld 25, 248(1) # restore R25 - ld 26, 256(1) # restore R26 - ld 27, 264(1) # restore R27 - ld 28, 272(1) # restore R28 - ld 29, 280(1) # restore R29 - ld 30, 288(1) # restore R30 - ld 31, 296(1) # restore R31 + ld 2, 0(1) # restore TOC + ld 14, 8(1) # restore R14 + ld 15, 16(1) # restore R15 + ld 16, 24(1) # restore R16 + ld 17, 32(1) # restore R17 + ld 18, 40(1) # restore R18 + ld 19, 48(1) # restore R19 + ld 20, 56(1) # restore R20 + ld 21, 64(1) # restore R21 + ld 22, 72(1) # restore R22 + ld 23, 80(1) # restore R23 + ld 24, 88(1) # restore R24 + ld 25, 96(1) # restore R25 + ld 26, 104(1) # restore R26 + ld 27, 112(1) # restore R27 + ld 28, 120(1) # restore R28 + ld 29, 128(1) # restore R29 + ld 30, 136(1) # restore R30 + ld 31, 144(1) # restore R31 + ld 3, 152(1) # restore hidden # restore CR - ld 0, 304(1) + ld 0, 160(1) mtcr 0 # restore LR - ld 0, 312(1) + ld 0, 168(1) mtlr 0 # load PC - ld 0, 320(1) + ld 0, 176(1) # restore CTR mtctr 0 # adjust stack - addi 1, 1, 328 + addi 1, 1, 184 + + # zero in r3 indicates first jump to context-function + cmpdi 3, 0 + beq use_entry_arg + + # return transfer_t + std 6, 0(3) + std 5, 8(3) + + # jump to context + bctr - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr 3, 6 + mr 4, 5 # jump to context bctr diff --git a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S index e134bae637e..a2f9a2f3bb3 100644 --- a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -60,14 +60,12 @@ .file "jump_riscv64_sysv_elf_gas.S" .text .align 1 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: # prepare stack for GP + FPU addi sp, sp, -0xd0 - beqz a3, .L1 - # save fs0 - fs11 fsd fs0, 0x00(sp) fsd fs1, 0x08(sp) @@ -81,7 +79,6 @@ jump_fcontext: fsd fs9, 0x48(sp) fsd fs10, 0x50(sp) fsd fs11, 0x58(sp) -.L1: # save s0-s11, ra sd s0, 0x60(sp) @@ -101,13 +98,12 @@ jump_fcontext: # save RA as PC sd ra, 0xc8(sp) - # store SP (pointing to context-data) in A0 - sd sp, (a0) + # store SP (pointing to context-data) in A2 + mv a2, sp - # restore SP (pointing to context-data) from A1 - mv sp, a1 + # restore SP (pointing to context-data) from A0 + mv sp, a0 - beqz a3, .L2 # load fs0 - fs11 fld fs0, 0x00(sp) fld fs1, 0x08(sp) @@ -121,7 +117,6 @@ jump_fcontext: fld fs9, 0x48(sp) fld fs10, 0x50(sp) fld fs11, 0x58(sp) -.L2: # load s0-s11,ra ld s0, 0x60(sp) @@ -138,7 +133,9 @@ jump_fcontext: ld s11, 0xb8(sp) ld ra, 0xc0(sp) - # use A2 as return value + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA mv a0, a2 # load pc @@ -148,6 +145,6 @@ jump_fcontext: addi sp, sp, 0xd0 jr a2 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S index ec01de8e1ff..14622086294 100644 --- a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S @@ -45,13 +45,13 @@ .register %g6,#ignore .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 4 -.type jump_fcontext,@function +.type swoole_jump_fcontext,@function // intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, +// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, // bool preserve_fpu = true); -jump_fcontext: +swoole_jump_fcontext: // %o0 = pointer to old fcontext, save current state here // %o1 = new context to jump to // %o2 = new return value in context %o0 @@ -133,7 +133,7 @@ Lno_fpu: jmp %o4 mov %o2, %o0 // return arg as result -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S deleted file mode 100644 index 5f85c3e57d4..00000000000 --- a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright Martin Husemann 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 -#ifdef __NetBSD__ -#define FLUSHW t 0x83; nop // T_FLUSHWIN -#endif - -.text -.globl jump_fcontext -.align 4 -.type jump_fcontext,@function -// intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, -// bool preserve_fpu = true); -jump_fcontext: - // %o0 = pointer to old fcontext, save current state here - // %o1 = new context to jump to - // %o2 = new return value in context %o0 - // %o3 = preserve fpu registers - // Save current state in %o0 fcontext, then activate %o1. - // If %o3, include fpu registers. - - FLUSHW // make sure all shadow registers are up to date in the current stack - - // save current state to fcontext_t at %o0 - st %sp, [%o0 + FC_GREG + OFF(0)] // current stack pointer - add %o7, 8, %o4 // calculate next instruction past call - st %o4, [%o0 + FC_GREG + OFF(1)] // and store it as %pc in save context - st %o7, [%o0 + FC_GREG + OFF(2)] - st %g1, [%o0 + FC_GREG + OFF(3)] - st %g2, [%o0 + FC_GREG + OFF(4)] - st %g3, [%o0 + FC_GREG + OFF(5)] - st %g6, [%o0 + FC_GREG + OFF(6)] - st %g7, [%o0 + FC_GREG + OFF(7)] - - // do we need to handle fpu? - cmp %o3, 0 - bz Lno_fpu - nop - - add %o0, FC_FPU, %o5 - std %f0, [%o5] - std %f2, [%o5+0x08] - std %f4, [%o5+0x10] - std %f6, [%o5+0x18] - std %f8, [%o5+0x20] - std %f10, [%o5+0x28] - std %f12, [%o5+0x30] - std %f14, [%o5+0x38] - st %fsr, [%o0+FC_FSR] - - add %o1, FC_FPU, %o5 - ldd [%o5], %f0 - ldd [%o5+0x08], %f2 - ldd [%o5+0x10], %f4 - ldd [%o5+0x18], %f6 - ldd [%o5+0x20], %f8 - ldd [%o5+0x28], %f10 - ldd [%o5+0x30], %f12 - ldd [%o5+0x38], %f14 - ld [%o1+FC_FSR], %fsr - -Lno_fpu: - // load new state from %o1 - ld [%o1 + FC_GREG + OFF(1)], %o4 - ld [%o1 + FC_GREG + OFF(2)], %o7 - ld [%o1 + FC_GREG + OFF(3)], %g1 - ld [%o1 + FC_GREG + OFF(4)], %g2 - ld [%o1 + FC_GREG + OFF(5)], %g3 - ld [%o1 + FC_GREG + OFF(6)], %g6 - ld [%o1 + FC_GREG + OFF(7)], %g7 - // switch to new stack - ld [%o1 + FC_GREG + OFF(0)], %sp - // and now reload from this stack the shadow regist bank contents - ld [%sp + OFF(0)], %l0 - ld [%sp + OFF(1)], %l1 - ld [%sp + OFF(2)], %l2 - ld [%sp + OFF(3)], %l3 - ld [%sp + OFF(4)], %l4 - ld [%sp + OFF(5)], %l5 - ld [%sp + OFF(6)], %l6 - ld [%sp + OFF(7)], %l7 - ld [%sp + OFF(8)], %i0 - ld [%sp + OFF(9)], %i1 - ld [%sp + OFF(10)], %i2 - ld [%sp + OFF(11)], %i3 - ld [%sp + OFF(12)], %i4 - ld [%sp + OFF(13)], %i5 - ld [%sp + OFF(14)], %i6 - ld [%sp + OFF(15)], %i7 - - // finally continue execution in new context - jmp %o4 - mov %o2, %o0 // return arg as result - -.size jump_fcontext,.-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm deleted file mode 100644 index c4706ef9913..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "jump_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl jump_fcontext -.def jump_fcontext; .scl 2; .type 32; .endef -.seh_proc jump_fcontext -jump_fcontext: -.seh_endprologue - - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %rsi /* save RSI */ - pushq %rdi /* save RDI */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* save current stack base */ - movq 0x08(%r10), %rax - pushq %rax - /* save current stack limit */ - movq 0x10(%r10), %rax - pushq %rax - /* save current deallocation stack */ - movq 0x1478(%r10), %rax - pushq %rax - /* save fiber local storage */ - movq 0x18(%r10), %rax - pushq %rax - - /* prepare stack for FPU */ - leaq -0xa8(%rsp), %rsp - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 1f - - /* save MMX control- and status-word */ - stmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fnstcw 0xa4(%rsp) - - /* save XMM storage */ - movaps %xmm6, (%rsp) - movaps %xmm7, 0x10(%rsp) - movaps %xmm8, 0x20(%rsp) - movaps %xmm9, 0x30(%rsp) - movaps %xmm10, 0x40(%rsp) - movaps %xmm11, 0x50(%rsp) - movaps %xmm12, 0x60(%rsp) - movaps %xmm13, 0x70(%rsp) - movaps %xmm14, 0x80(%rsp) - movaps %xmm15, 0x90(%rsp) - -1: - /* set R10 to zero */ - xorq %r10, %r10 - /* set indicator */ - pushq %r10 - - /* store RSP (pointing to context-data) in RCX */ - movq %rsp, (%rcx) - - /* restore RSP (pointing to context-data) from RDX */ - movq %rdx, %rsp - - /* load indicator */ - popq %r10 - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fldcw 0xa4(%rsp) - - /* restore XMM storage */ - movaps (%rsp), %xmm6 - movaps 0x10(%rsp), %xmm7 - movaps 0x20(%rsp), %xmm8 - movaps 0x30(%rsp), %xmm9 - movaps 0x40(%rsp), %xmm10 - movaps 0x50(%rsp), %xmm11 - movaps 0x60(%rsp), %xmm12 - movaps 0x70(%rsp), %xmm13 - movaps 0x80(%rsp), %xmm14 - movaps 0x90(%rsp), %xmm15 - -2: - /* set offset of stack */ - movq 0xa8, %rcx - - /* test for indicator */ - testq %r10, %r10 - je 3f - - addq 0x8, %rcx - -3: - /* prepare stack for FPU */ - leaq (%rsp,%rcx), %rsp - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* restore fiber local storage */ - popq %rax - movq %rax, 0x18(%r10) - /* restore deallocation stack */ - popq %rax - movq %rax, 0x1478(%r10) - /* restore stack limit */ - popq %rax - movq %rax, 0x10(%r10) - /* restore stack base */ - popq %rax - movq %rax, 0x8(%r10) - - popq %r12 /* restore R12 */ - popq %r13 /* restore R13 */ - popq %r14 /* restore R14 */ - popq %r15 /* restore R15 */ - popq %rdi /* restore RDI */ - popq %rsi /* restore RSI */ - popq %rbx /* restore RBX */ - popq %rbp /* restore RBP */ - - /* restore return-address */ - popq %r10 - - /* use third arg as return-value after jump */ - movq %r8, %rax - /* use third arg as first arg in context function */ - movq %r8, %rcx - - /* indirect jump to context */ - jmp *%r10 -.seh_endproc diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm deleted file mode 100644 index 6950ee7ff6c..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,216 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - .endprolog - - push rbp ; save RBP - push rbx ; save RBX - push rsi ; save RSI - push rdi ; save RDI - push r15 ; save R15 - push r14 ; save R14 - push r13 ; save R13 - push r12 ; save R12 - - ; load NT_TIB - mov r10, gs:[030h] - ; save current stack base - mov rax, [r10+08h] - push rax - ; save current stack limit - mov rax, [r10+010h] - push rax - ; save current deallocation stack - mov rax, [r10+01478h] - push rax - ; save fiber local storage - mov rax, [r10+018h] - push rax - - ; prepare stack for FPU - lea rsp, [rsp-0a8h] - - ; test for flag preserve_fpu - test r9, r9 - je nxt1 - - ; save MMX control- and status-word - stmxcsr [rsp+0a0h] - ; save x87 control-word - fnstcw [rsp+0a4h] - - ; save XMM storage - movaps [rsp], xmm6 - movaps [rsp+010h], xmm7 - movaps [rsp+020h], xmm8 - movaps [rsp+030h], xmm9 - movaps [rsp+040h], xmm10 - movaps [rsp+050h], xmm11 - movaps [rsp+060h], xmm12 - movaps [rsp+070h], xmm13 - movaps [rsp+080h], xmm14 - movaps [rsp+090h], xmm15 - -nxt1: - ; set R10 to zero - xor r10, r10 - ; set indicator - push r10 - - ; store RSP (pointing to context-data) in RCX - mov [rcx], rsp - - ; restore RSP (pointing to context-data) from RDX - mov rsp, rdx - - ; load indicator - pop r10 - - ; test for flag preserve_fpu - test r9, r9 - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [rsp+0a0h] - ; save x87 control-word - fldcw [rsp+0a4h] - - ; restore XMM storage - movaps xmm6, [rsp] - movaps xmm7, [rsp+010h] - movaps xmm8, [rsp+020h] - movaps xmm9, [rsp+030h] - movaps xmm10, [rsp+040h] - movaps xmm11, [rsp+050h] - movaps xmm12, [rsp+060h] - movaps xmm13, [rsp+070h] - movaps xmm14, [rsp+080h] - movaps xmm15, [rsp+090h] - -nxt2: - ; set offset of stack - mov rcx, 0a8h - - ; test for indicator - test r10, r10 - je nxt3 - - add rcx, 08h - -nxt3: - ; prepare stack for FPU - lea rsp, [rsp+rcx] - - ; load NT_TIB - mov r10, gs:[030h] - ; restore fiber local storage - pop rax - mov [r10+018h], rax - ; restore deallocation stack - pop rax - mov [r10+01478h], rax - ; restore stack limit - pop rax - mov [r10+010h], rax - ; restore stack base - pop rax - mov [r10+08h], rax - - pop r12 ; restore R12 - pop r13 ; restore R13 - pop r14 ; restore R14 - pop r15 ; restore R15 - pop rdi ; restore RDI - pop rsi ; restore RSI - pop rbx ; restore RBX - pop rbp ; restore RBP - - ; restore return-address - pop r10 - - ; use third arg as return-value after jump - mov rax, r8 - ; use third arg as first arg in context function - mov rcx, r8 - - ; indirect jump to context - jmp r10 -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index 893b094be00..7b80132b67e 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -12,89 +12,131 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "jump_x86_64_sysv_elf_gas.S" .text -.globl jump_fcontext -.type jump_fcontext,@function +.globl swoole_jump_fcontext +.type swoole_jump_fcontext,@function .align 16 -jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp +swoole_jump_fcontext: + _CET_ENDBR + leaq -0x40(%rsp), %rsp /* prepare stack */ - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x10(%rsp) /* save R12 */ + movq %r13, 0x18(%rsp) /* save R13 */ + movq %r14, 0x20(%rsp) /* save R14 */ + movq %r15, 0x28(%rsp) /* save R15 */ + movq %rbx, 0x30(%rsp) /* save RBX */ + movq %rbp, 0x38(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp +#if SWOOLE_CONTEXT_SHADOW_STACK + /* grow the stack to reserve space for shadow stack pointer(SSP) */ + leaq -0x8(%rsp), %rsp + /* read the current SSP and store it */ + rdsspq %rcx + movq %rcx, (%rsp) +#endif - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp -2: - /* prepare stack for FPU */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* first 8 bytes are SSP */ + movq (%rsp), %rcx leaq 0x8(%rsp), %rsp - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + /* Restore target(new) shadow stack */ + rstorssp -8(%rcx) + /* restore token for previous shadow stack is pushed */ + /* on previous shadow stack after saveprevssp */ + saveprevssp + + /* when return, swoole_jump_fcontext jump to restored return address */ + /* (r8) instead of RET. This miss of RET implies us to unwind */ + /* shadow stack accordingly. Otherwise mismatch occur */ + movq $1, %rcx + incsspq %rcx +#endif + + movq 0x40(%rsp), %r8 /* restore return-address */ - /* restore return-address */ - popq %r8 +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq 0x8(%rsp), %rdx /* load stack guard */ + movq %rdx, %fs:0x28 /* restore stack guard to TLS record */ +#endif + + movq 0x10(%rsp), %r12 /* restore R12 */ + movq 0x18(%rsp), %r13 /* restore R13 */ + movq 0x20(%rsp), %r14 /* restore R14 */ + movq 0x28(%rsp), %r15 /* restore R15 */ + movq 0x30(%rsp), %rbx /* restore RBX */ + movq 0x38(%rsp), %rbp /* restore RBP */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + leaq 0x48(%rsp), %rsp /* prepare stack */ + + /* return transfer_t from jump */ +#if !defined(_ILP32) + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx +#else + /* RAX == data:fctx */ + salq $32, %rsi + orq %rsi, %rax +#endif + /* pass transfer_t as first arg in context function */ +#if !defined(_ILP32) + /* RDI == fctx, RSI == data */ +#else + /* RDI == data:fctx */ +#endif + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S index 50563c323e4..0bf18bd763f 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S @@ -21,73 +21,55 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 8 -_jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp - - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +_swoole_jump_fcontext: + leaq -0x38(%rsp), %rsp /* prepare stack */ - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + movq 0x38(%rsp), %r8 /* restore return-address */ -2: - /* prepare stack for FPU */ - leaq 0x8(%rsp), %rsp +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ - /* restore return-address */ - popq %r8 + leaq 0x40(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 diff --git a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S index 5fa092eef5d..fd98d15984d 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,19 +51,19 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "make_arm64_aapcs_elf_gas.S" .text .align 2 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: # shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF # reserve space for context-data on context-stack sub x0, x0, #0xb0 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # store address as a PC to jump in str x2, [x0, #0xa0] @@ -80,8 +80,6 @@ finish: # exit application bl _exit -.size make_fcontext,.-make_fcontext -#ifndef __NetBSD__ +.size swoole_make_fcontext,.-swoole_make_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index cd8b9742358..7977c0ee9bd 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -45,25 +51,21 @@ * * *******************************************************/ - .text -.globl _make_fcontext +.globl _swoole_make_fcontext .balign 16 -_make_fcontext: +_swoole_make_fcontext: ; shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF ; reserve space for context-data on context-stack sub x0, x0, #0xb0 - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function ; store address as a PC to jump in str x2, [x0, #0xa0] - ; compute abs address of label finish - ; 0x0c = 3 instructions * size (4) before label 'finish' - adr x1, finish ; save address of finish as return-address for context-function diff --git a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S deleted file mode 100644 index 1606dbb0fd8..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,%function -make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #104 - - @ third arg of make_fcontext() == address of context-function - str a3, [a1,#100] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#96] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl _exit@PLT -.size make_fcontext,.-make_fcontext - -#ifndef __NetBSD__ -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S deleted file mode 100644 index 039726f02c9..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #108 - - @ third arg of make_fcontext() == address of context-function - str a3, [a1,#104] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#100] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl __exit diff --git a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm deleted file mode 100644 index 5c3d6df2c4d..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,86 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - - AREA |.text|, CODE - ALIGN 4 - EXPORT make_fcontext - IMPORT _exit - -make_fcontext PROC - ; first arg of make_fcontext() == top of context-stack - ; save top of context-stack (base) A4 - mov a4, a1 - - ; shift address in A1 to lower 16 byte boundary - bic a1, a1, #0x0f - - ; reserve space for context-data on context-stack - sub a1, a1, #0x74 - - ; save top address of context_stack as 'base' - str a4, [a1,#0x48] - ; second arg of make_fcontext() == size of context-stack - ; compute bottom address of context-stack (limit) - sub a4, a4, a2 - ; save bottom address of context-stack as 'limit' - str a4, [a1,#0x44] - ; save bottom address of context-stack as 'dealloction stack' - str a4, [a1,#0x40] - - ; third arg of make_fcontext() == address of context-function - str a3, [a1,#0x70] - - ; compute abs address of label finish - adr a2, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - str a2, [a1,#0x6c] - - bx lr ; return pointer to context-data - -finish - ; exit code is zero - mov a1, #0 - ; exit application - bl _exit - - ENDP - END diff --git a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S index b22fa7ebe92..226dd9a16d5 100644 --- a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S @@ -7,16 +7,10 @@ // Stub file for universal binary -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +#if defined(__x86_64__) #include "make_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_macho_gas.S" -#elif defined(__arm__) - #include "make_arm_aapcs_macho_gas.S" #elif defined(__arm64__) #include "make_arm64_aapcs_macho_gas.S" #else diff --git a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm b/thirdparty/boost/asm/make_i386_ms_pe_gas.asm deleted file mode 100644 index 881b3c81e95..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -*******************************************************************/ - -.file "make_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _make_fcontext -.def _make_fcontext; .scl 2; .type 32; .endef -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %eax - - /* reserve space for first argument of context-function */ - /* EAX might already point to a 16byte border */ - leal -0x08(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - /* additional space is required for SEH */ - leal -0x3c(%eax), %eax - - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %ecx - /* save top address of context stack as 'base' */ - movl %ecx, 0x14(%eax) - /* second arg of make_fcontext() == size of context-stack */ - movl 0x08(%esp), %edx - /* negate stack size for LEA instruction (== substraction) */ - negl %edx - /* compute bottom address of context stack (limit) */ - leal (%ecx,%edx), %ecx - /* save bottom address of context-stack as 'limit' */ - movl %ecx, 0x10(%eax) - /* save bottom address of context-stack as 'dealloction stack' */ - movl %ecx, 0xc(%eax) - - /* third arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %ecx - movl %ecx, 0x2c(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x04(%eax) - - /* compute abs address of label finish */ - movl $finish, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x30(%eax) - - /* traverse current seh chain to get the last exception handler installed by Windows */ - /* note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default */ - /* the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler */ - /* at its end by RaiseException all seh andlers are disregarded if not present and the */ - /* program is aborted */ - /* load NT_TIB into ECX */ - movl %fs:(0x0), %ecx - -walk: - /* load 'next' member of current SEH into EDX */ - movl (%ecx), %edx - /* test if 'next' of current SEH is last (== 0xffffffff) */ - incl %edx - jz found - decl %edx - /* exchange content; ECX contains address of next SEH */ - xchgl %ecx, %edx - /* inspect next SEH */ - jmp walk - -found: - /* load 'handler' member of SEH == address of last SEH handler installed by Windows */ - movl 0x04(%ecx), %ecx - /* save address in ECX as SEH handler for context */ - movl %ecx, 0x3c(%eax) - /* set ECX to -1 */ - movl $0xffffffff, %ecx - /* save ECX as next SEH item */ - movl %ecx, 0x38(%eax) - /* load address of next SEH item */ - leal 0x38(%eax), %ecx - /* save next SEH */ - movl %ecx, 0x18(%eax) - - /* return pointer to context-data */ - ret - -finish: - /* ESP points to same address as ESP on entry of context function + 0x4 */ - xorl %eax, %eax - /* exit code is zero */ - movl %eax, (%esp) - /* exit application */ - call __exit - hlt - -.def __exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm b/thirdparty/boost/asm/make_i386_ms_pe_masm.asm deleted file mode 100644 index 35c8b1cf447..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm +++ /dev/null @@ -1,122 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -; standard C library function -_exit PROTO, value:SDWORD -.code - -make_fcontext PROC BOOST_CONTEXT_EXPORT - ; first arg of make_fcontext() == top of context-stack - mov eax, [esp+04h] - - ; reserve space for first argument of context-function - ; EAX might already point to a 16byte border - lea eax, [eax-08h] - - ; shift address in EAX to lower 16 byte boundary - and eax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. EIP + return-address for context-function - ; on context-function entry: (ESP -0x4) % 8 == 0 - ; additional space is required for SEH - lea eax, [eax-03ch] - - ; first arg of make_fcontext() == top of context-stack - mov ecx, [esp+04h] - ; save top address of context stack as 'base' - mov [eax+014h], ecx - ; second arg of make_fcontext() == size of context-stack - mov edx, [esp+08h] - ; negate stack size for LEA instruction (== substraction) - neg edx - ; compute bottom address of context stack (limit) - lea ecx, [ecx+edx] - ; save bottom address of context-stack as 'limit' - mov [eax+010h], ecx - ; save bottom address of context-stack as 'dealloction stack' - mov [eax+0ch], ecx - - ; third arg of make_fcontext() == address of context-function - mov ecx, [esp+0ch] - mov [eax+02ch], ecx - - ; save MMX control- and status-word - stmxcsr [eax] - ; save x87 control-word - fnstcw [eax+04h] - - ; compute abs address of label finish - mov ecx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [eax+030h], ecx - - ; traverse current seh chain to get the last exception handler installed by Windows - ; note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default - ; the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler - ; at its end by RaiseException all seh-handlers are disregarded if not present and the - ; program is aborted - assume fs:nothing - ; load NT_TIB into ECX - mov ecx, fs:[0h] - assume fs:error - -walk: - ; load 'next' member of current SEH into EDX - mov edx, [ecx] - ; test if 'next' of current SEH is last (== 0xffffffff) - inc edx - jz found - dec edx - ; exchange content; ECX contains address of next SEH - xchg edx, ecx - ; inspect next SEH - jmp walk - -found: - ; load 'handler' member of SEH == address of last SEH handler installed by Windows - mov ecx, [ecx+04h] - ; save address in ECX as SEH handler for context - mov [eax+03ch], ecx - ; set ECX to -1 - mov ecx, 0ffffffffh - ; save ECX as next SEH item - mov [eax+038h], ecx - ; load address of next SEH item - lea ecx, [eax+038h] - ; save next SEH - mov [eax+018h], ecx - - ret ; return pointer to context-data - -finish: - ; exit code is zero - xor eax, eax - mov [esp], eax - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S b/thirdparty/boost/asm/make_i386_sysv_elf_gas.S deleted file mode 100644 index 26c94513448..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* third arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - call 2f - /* address of label 2 */ -2: popl %ebx - /* compute address of GOT and store it in EBX */ - addl $_GLOBAL_OFFSET_TABLE_+[.-2b], %ebx - - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call _exit@PLT - hlt -.size make_fcontext,.-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_sysv_macho_gas.S deleted file mode 100644 index 02ae2230e04..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* thrid arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call __exit - hlt diff --git a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index e364b2db62f..00000000000 --- a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "make_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..5359e0235e1 --- /dev/null +++ b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S @@ -0,0 +1,72 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 2 +.type swoole_make_fcontext,@function +swoole_make_fcontext: + # shift address in A0 to lower 16 byte boundary + bstrins.d $a0, $zero, 3, 0 + + # reserve space for context-data on context-stack + addi.d $a0, $a0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + st.d $a2, $a0, 152 + + # save address of finish as return-address for context-function + # will be entered after context-function returns + la.local $a4, finish + st.d $a4, $a0, 144 + + # return pointer to context-data + jr $ra + +finish: + # exit code is zero + li.d $a0, 0 + # call _exit(0) + b %plt(_exit) + +.size swoole_make_fcontext, .-swoole_make_fcontext +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S b/thirdparty/boost/asm/make_mips32_o32_elf_gas.S deleted file mode 100644 index c71ad05fbd9..00000000000 --- a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: -#ifdef __PIC__ -.set noreorder -.cpload $t9 -.set reorder -#endif - # first arg of make_fcontext() == top address of context-stack - move $v0, $a0 - - # shift address in A0 to lower 16 byte boundary - move $v1, $v0 - li $v0, -16 # 0xfffffffffffffff0 - and $v0, $v1, $v0 - - # reserve space for context-data on context-stack - # including 48 byte of shadow space (sp % 16 == 0) - addiu $v0, $v0, -140 - - # third arg of make_fcontext() == address of context-function - sw $a2, 88($v0) - # save global pointer in context-data - # S0 will contain address of global pointer - sw $gp, 48($v0) - - # compute abs address of label finish - la $t9, finish - # save address of finish as return-address for context-function - # will be entered after context-function returns - sw $t9, 84($v0) - - jr $ra # return pointer to context-data - -finish: - # allocate stack space (contains shadow space for subroutines) - addiu $sp, $sp, -32 - # save return address - sw $ra, 28($sp) - - # restore GP (global pointer) - move $gp, $s0 - # exit code is zero - move $a0, $zero - # address of exit - lw $t9, %call16(_exit)($gp) - # exit application - jalr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S index 8899e3d203b..d3d46313b6b 100644 --- a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -45,12 +45,13 @@ * * * *****************************************************/ +.file "make_mips64_n64_elf_gas.S" .text -.globl make_fcontext +.globl swoole_make_fcontext .align 3 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: +.type swoole_make_fcontext,@function +.ent swoole_make_fcontext +swoole_make_fcontext: #ifdef __PIC__ .set noreorder .cpload $t9 @@ -63,7 +64,7 @@ make_fcontext: # reserve space for context-data on context-stack daddiu $v0, $v0, -160 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function sd $a2, 152($v0) # save global pointer in context-data sd $gp, 136($v0) @@ -88,8 +89,8 @@ finish: dla $t9, _exit move $a0, $zero jr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext +.end swoole_make_fcontext +.size swoole_make_fcontext, .-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index 52e72209337..00000000000 --- a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "make_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S deleted file mode 100644 index 043bc4ac4e1..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -make_fcontext: - # save return address into R6 - mflr %r6 - - # first arg of make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi %r3, %r3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 304 - - # third arg of make_fcontext() == address of context-function - stw %r5, 236(%r3) - - # load LR - mflr %r0 - # jump to label 1 - bl 1f -1: - # load LR into R4 - mflr %r4 - # compute abs address of label finish - addi %r4, %r4, finish - 1b - # restore LR - mtlr %r0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw %r4, 232(%r3) - - # restore return address from R6 - mtlr %r6 - - blr # return pointer to context-data - -finish: - # save return address into R0 - mflr %r0 - # save return address on stack, set up stack frame - stw %r0, 4(%r1) - # allocate stack space, R1 % 16 == 0 - stwu %r1, -16(%r1) - - # exit code is zero - li %r3, 0 - # exit application - bl _exit@plt -.size make_fcontext, .-make_fcontext - -#ifndef __NetBSD__ -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S deleted file mode 100644 index f99b883f074..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - ; save return address into R6 - mflr r6 - - ; first arg of make_fcontext() == top address of context-function - ; shift address in R3 to lower 16 byte boundary - clrrwi r3, r3, 4 - - ; reserve space for context-data on context-stack - ; including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi r3, r3, 304 - - ; third arg of make_fcontext() == address of context-function - stw r5, 236(r3) - - ; load LR - mflr r0 - ; jump to label 1 - bl l1 -l1: - ; load LR into R4 - mflr r4 - ; compute abs address of label finish - addi r4, r4, lo16((finish - .)+4) - # restore LR - mtlr r0 - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - stw r4, 232(r3) - - ; restore return address from R6 - mtlr r6 - - blr ; return pointer to context-data - -finish: - ; save return address into R0 - mflr r0 - ; save return address on stack, set up stack frame - stw r0, 4(r1) - ; allocate stack space, R1 % 16 == 0 - stwu r1, -16(r1) - - ; exit code is zero - li r3, 0 - ; exit application - bl __exit diff --git a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index 5298d494a4b..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,55 +0,0 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect make_fcontext[DS] -make_fcontext: - .long .make_fcontext[PR] - .csect .make_fcontext[PR], 3 -#.make_fcontext: - # save return address into R6 - mflr 6 - - # first arg of make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 304 - - # third arg of make_fcontext() == address of context-function - stw 5, 236(3) - - # load LR - mflr 0 - # jump to label 1 - bl .Label -.Label: - # load LR into R4 - mflr 4 - # compute abs address of label .L_finish - addi 4, 4, .L_finish - .Label - # restore LR - mtlr 0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw 4, 232(3) - - # restore return address from R6 - mtlr 6 - - blr # return pointer to context-data - -.L_finish: - # save return address into R0 - mflr 0 - # save return address on stack, set up stack frame - stw 0, 4(1) - # allocate stack space, R1 % 16 == 0 - stwu 1, -16(1) - - # exit code is zero - li 3, 0 - # exit application - bl ._exit - nop diff --git a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S index 35c49442406..59354f8dde5 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S @@ -12,134 +12,122 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ -.globl make_fcontext +.file "make_ppc64_sysv_elf_gas.S" +.globl swoole_make_fcontext #if _CALL_ELF == 2 .text .align 2 -make_fcontext: - addis %r2, %r12, .TOC.-make_fcontext@ha - addi %r2, %r2, .TOC.-make_fcontext@l - .localentry make_fcontext, . - make_fcontext +swoole_make_fcontext: + addis %r2, %r12, .TOC.-swoole_make_fcontext@ha + addi %r2, %r2, .TOC.-swoole_make_fcontext@l + .localentry swoole_make_fcontext, . - swoole_make_fcontext #else .section ".opd","aw" .align 3 -make_fcontext: +swoole_make_fcontext: # ifdef _CALL_LINUX - .quad .L.make_fcontext,.TOC.@tocbase,0 - .type make_fcontext,@function + .quad .L.swoole_make_fcontext,.TOC.@tocbase,0 + .type swoole_make_fcontext,@function .text .align 2 -.L.make_fcontext: +.L.swoole_make_fcontext: # else - .hidden .make_fcontext - .globl .make_fcontext - .quad .make_fcontext,.TOC.@tocbase,0 - .size make_fcontext,24 - .type .make_fcontext,@function + .hidden .swoole_make_fcontext + .globl .swoole_make_fcontext + .quad .swoole_make_fcontext,.TOC.@tocbase,0 + .size swoole_make_fcontext,24 + .type .swoole_make_fcontext,@function .text .align 2 -.make_fcontext: +.swoole_make_fcontext: # endif #endif # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-stack + # first arg of swoole_make_fcontext() == top address of context-stack # shift address in R3 to lower 16 byte boundary clrrdi %r3, %r3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 392 + subi %r3, %r3, 248 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point - std %r5, 320(%r3) + std %r5, 176(%r3) #else # save address of context-function entry point ld %r4, 0(%r5) - std %r4, 320(%r3) + std %r4, 176(%r3) # save TOC of context-function ld %r4, 8(%r5) - std %r4, 152(%r3) + std %r4, 0(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + +#if _CALL_ELF != 2 + # zero in r3 indicates first jump to context-function + std %r0, 152(%r3) #endif # load LR @@ -155,7 +143,7 @@ make_fcontext: mtlr %r0 # save address of finish as return-address for context-function # will be entered after context-function returns - std %r4, 312(%r3) + std %r4, 168(%r3) # restore return address from R6 mtlr %r6 @@ -176,16 +164,14 @@ finish: bl _exit nop #if _CALL_ELF == 2 - .size make_fcontext, .-make_fcontext + .size swoole_make_fcontext, .-swoole_make_fcontext #else # ifdef _CALL_LINUX - .size .make_fcontext, .-.L.make_fcontext + .size .swoole_make_fcontext, .-.L.swoole_make_fcontext # else - .size .make_fcontext, .-.make_fcontext + .size .swoole_make_fcontext, .-.swoole_make_fcontext # endif #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S index c4208c550e1..717f3bb2cf2 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S @@ -12,98 +12,85 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text -.globl _make_fcontext -_make_fcontext: +.globl _swoole_make_fcontext +_swoole_make_fcontext: ; save return address into R6 mflr r6 - ; first arg of make_fcontext() == top address of context-function + ; first arg of swoole_make_fcontext() == top address of context-function ; shift address in R3 to lower 16 byte boundary clrrwi r3, r3, 4 ; reserve space for context-data on context-stack ; including 64 byte of linkage + parameter area (R1 16 == 0) - subi r3, r3, 392 + subi r3, r3, 240 + + ; third arg of swoole_make_fcontext() == address of context-function + stw r5, 176(r3) + + ; set back-chain to zero + li r0, 0 + std r0, 184(r3) - ; third arg of make_fcontext() == address of context-function - stw r5, 320(r3) + ; compute address of returned transfer_t + addi r0, r3, 224 + mr r4, r0 + std r4, 152(r3) ; load LR mflr r0 @@ -118,7 +105,7 @@ l1: mtlr r0 ; save address of finish as return-address for context-function ; will be entered after context-function returns - std r4, 312(r3) + std r4, 168(r3) ; restore return address from R6 mtlr r6 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S index 8de630ca83f..58fd12eb916 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S @@ -1,22 +1,106 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect .make_fcontext[PR], 3 - .globl _make_fcontext -#._make_fcontext: +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "make_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext + .csect swoole_make_fcontext[DS], 3 +swoole_make_fcontext: + .llong .swoole_make_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_make_fcontext: # save return address into R6 mflr 6 - # first arg of make_fcontext() == top address of context-function + # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 + clrrdi 3, 3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 392 + subi 3, 3, 248 + + # third arg of swoole_make_fcontext() == address of context-function descriptor + ld 4, 0(5) + std 4, 176(3) + # save TOC of context-function + ld 4, 8(5) + std 4, 0(3) + + # set back-chain to zero + li 0, 0 + std 0, 184(3) - # third arg of make_fcontext() == address of context-function - stw 5, 320(3) + # zero in r3 indicates first jump to context-function + std 0, 152(3) # load LR mflr 0 @@ -31,7 +115,7 @@ mtlr 0 # save address of finish as return-address for context-function # will be entered after context-function returns - stw 4, 312(3) + std 4, 168(3) # restore return address from R6 mtlr 6 @@ -42,9 +126,9 @@ # save return address into R0 mflr 0 # save return address on stack, set up stack frame - stw 0, 8(1) + std 0, 8(1) # allocate stack space, R1 % 16 == 0 - stwu 1, -32(1) + stdu 1, -32(1) # exit code is zero li 3, 0 diff --git a/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S index 5322e0fdbde..cd43c775447 100644 --- a/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S @@ -60,16 +60,16 @@ .file "make_riscv64_sysv_elf_gas.S" .text .align 1 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: # shift address in a0 (allocated stack) to lower 16 byte boundary andi a0, a0, ~0xF # reserve space for context-data on context-stack addi a0, a0, -0xd0 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # store address as a PC to jump in sd a2, 0xc8(a0) @@ -86,6 +86,6 @@ finish: # exit application tail _exit@plt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S index 113e0f50f9a..1db988d2032 100644 --- a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S @@ -45,12 +45,12 @@ .register %g6,#ignore .text -.globl make_fcontext +.globl swoole_make_fcontext .align 4 -.type make_fcontext,@function +.type swoole_make_fcontext,@function // fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: +// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) +swoole_make_fcontext: save %sp, -CCFSZ, %sp // %i0 initial stack pointer // %i1 stack size limit @@ -83,7 +83,7 @@ finish: call _exit nop -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S deleted file mode 100644 index 2633471f2f1..00000000000 --- a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Martin Husemann 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 - -.text -.globl make_fcontext -.align 4 -.type make_fcontext,@function -// fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: - save %sp, -CCFSZ, %sp - // %i0 initial stack pointer - // %i1 stack size limit - // %i2 function pointer for context start function - - sub %i0, FC_SZ, %i4 // allocate fcontext_t at on the new stack and keep pointer as return value - andn %i4, BLOCK_SIZE-1, %i5 // force block ops usable alignement and keep pointer to fcontext in %i5 - - st %i0, [%i5+FC_stK+OFF(0)] // save fs_stack.sp - st %i1, [%i5+FC_stK+OFF(1)] // save fs_stack.size - sub %i5, CCFSZ, %o1 // leave space for one register window - st %o1, [%i5+FC_GREG+OFF(0)] // save new stack pointer - st %i2, [%i5+FC_GREG+OFF(1)] // save new %pc (function pointer) - st %g1, [%i5+FC_GREG+OFF(3)] - st %g2, [%i5+FC_GREG+OFF(4)] - st %g3, [%i5+FC_GREG+OFF(5)] - st %g6, [%i5+FC_GREG+OFF(6)] - st %g7, [%i5+FC_GREG+OFF(7)] - - // synthesize "return address": jump to finish - mov %i7, %l0 -2: call 3f - nop -3: add finish-2b-8, %o7, %i4 - st %i4, [%i5+FC_GREG+OFF(2)] - - ret - restore %g0, %i5, %o0 // return fcontext_t - -finish: - mov %g0, %o0 - call _exit - nop - -.size make_fcontext,.-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm deleted file mode 100644 index 31dd56645d5..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "make_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl make_fcontext -.def make_fcontext; .scl 2; .type 32; .endef -.seh_proc make_fcontext -make_fcontext: -.seh_endprologue - - /* first arg of make_fcontext() == top of context-stack */ - movq %rcx, %rax - - /* reserve 32byte shadow-space for context-function */ - leaq -0x28(%rax), %rax - - /* shift address in RAX to lower 16 byte boundary */ - /* == pointer to fcontext_t and address of context stack */ - andq $-16, %rax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ - /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x128(%rax), %rax - - /* third arg of make_fcontext() == address of context-function */ - movq %r8, 0x118(%rax) - - /* first arg of make_fcontext() == top of context-stack */ - /* save top address of context stack as 'base' */ - movq %rcx, 0xd0(%rax) - /* second arg of make_fcontext() == size of context-stack */ - /* negate stack size for LEA instruction (== substraction) */ - negq %rdx - /* compute bottom address of context stack (limit) */ - leaq (%rcx,%rdx), %rcx - /* save bottom address of context stack as 'limit' */ - movq %rcx, 0xc8(%rax) - /* save address of context stack limit as 'dealloction stack' */ - movq %rcx, 0xc0(%rax) - - /* save MMX control- and status-word */ - stmxcsr 0xa8(%rax) - /* save x87 control-word */ - fnstcw 0xac(%rax) - - /* compute abs address of label finish */ - leaq finish(%rip), %rcx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movq %rcx, 0x120(%rax) - - /* set indicator */ - movq 1, %rcx - movq %rcx, (%rax) - - ret /* return pointer to context-data */ - -finish: - /* 32byte shadow-space for _exit() are */ - /* already reserved by make_fcontext() */ - /* exit code is zero */ - xorq %rcx, %rcx - /* exit application */ - call _exit - hlt -.seh_endproc - -.def _exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm deleted file mode 100644 index a4eb58c021a..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,144 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -; standard C library function -EXTERN _exit:PROC -.code - -; generate function table entry in .pdata and unwind information in -make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - ; .xdata for a function's structured exception handling unwind behavior - .endprolog - - ; first arg of make_fcontext() == top of context-stack - mov rax, rcx - - ; reserve 32byte shadow-space for context-function - sub rax, 028h - - ; shift address in RAX to lower 16 byte boundary - ; == pointer to fcontext_t and address of context stack - and rax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. RIP + return-address for context-function - ; on context-function entry: (RSP -0x8) % 16 == 0 - sub rax, 0128h - - ; third arg of make_fcontext() == address of context-function - mov [rax+0118h], r8 - - ; first arg of make_fcontext() == top of context-stack - ; save top address of context stack as 'base' - mov [rax+0d0h], rcx - ; second arg of make_fcontext() == size of context-stack - ; negate stack size for LEA instruction (== substraction) - neg rdx - ; compute bottom address of context stack (limit) - lea rcx, [rcx+rdx] - ; save bottom address of context stack as 'limit' - mov [rax+0c8h], rcx - ; save address of context stack limit as 'dealloction stack' - mov [rax+0c0h], rcx - - ; save MMX control- and status-word - stmxcsr [rax+0a8h] - ; save x87 control-word - fnstcw [rax+0ach] - - ; compute abs address of label finish - lea rcx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [rax+0120h], rcx - - ; set indicator - mov rcx, 1 - mov [rax], rcx - - ret ; return pointer to context-data - -finish: - ; 32byte shadow-space for _exit() are - ; already reserved by make_fcontext() - ; exit code is zero - xor rcx, rcx - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index 335117a22ee..00674735f38 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -12,66 +12,136 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "make_x86_64_sysv_elf_gas.S" .text -.globl make_fcontext -.type make_fcontext,@function +.globl swoole_make_fcontext +.type swoole_make_fcontext,@function .align 16 -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +swoole_make_fcontext: + _CET_ENDBR +#if SWOOLE_CONTEXT_SHADOW_STACK + /* the new shadow stack pointer (SSP) */ + movq -0x8(%rdi), %r9 +#endif + + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* third arg of swoole_make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x30(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + /* save stack guard */ + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x40(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x38(%rax) + +#if SWOOLE_CONTEXT_SHADOW_STACK + /* Populate the shadow stack and normal stack */ + /* get original SSP */ + rdsspq %r8 + /* restore new shadow stack */ + rstorssp -0x8(%r9) + /* save the restore token on the original shadow stack */ + saveprevssp + /* push the address of "jmp trampoline" to the new shadow stack */ + /* as well as the stack */ + call 1f + jmp trampoline +1: + /* save address of "jmp trampoline" as return-address */ + /* for context-function */ + pop 0x38(%rax) + /* Get the new SSP. */ + rdsspq %r9 + /* restore original shadow stack */ + rstorssp -0x8(%r8) + /* save the restore token on the new shadow stack. */ + saveprevssp + + /* reserve space for the new SSP */ + leaq -0x8(%rax), %rax + /* save the new SSP to this fcontext */ + movq %r9, (%rax) +#endif ret /* return pointer to context-data */ +trampoline: + _CET_ENDBR + /* store return address on stack */ + /* fix stack alignment */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* save address of "jmp *%rbp" as return-address */ + /* on stack and shadow stack */ + call 2f + jmp *%rbp +2: +#else + push %rbp +#endif + /* jump to context-function */ + jmp *%rbx + finish: + _CET_ENDBR /* exit code is zero */ xorq %rdi, %rdi /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S index 3a030f8420d..46f8bd15cc4 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S @@ -21,48 +21,53 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 8 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ - movabs $-16, %r8 - andq %r8, %rax + andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x48(%rax), %rax + leaq -0x40(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* third arg of swoole_make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x28(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x30(%rax) ret /* return pointer to context-data */ +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + finish: /* exit code is zero */ xorq %rdi, %rdi diff --git a/thirdparty/hiredis/hiredis.c b/thirdparty/hiredis/hiredis.c index 0a01e68754c..41298addcd0 100644 --- a/thirdparty/hiredis/hiredis.c +++ b/thirdparty/hiredis/hiredis.c @@ -42,6 +42,7 @@ #include "net.h" #include "sds.h" +#define SW_HOOK_POLL_FAKE #include "swoole_socket_hook.h" extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout); diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index ec4dee4984d..96539faf816 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -9,282 +9,380 @@ #include #include -static void multipart_log(const char * format, ...) -{ #ifdef DEBUG_MULTIPART - va_list args; - va_start(args, format); - - fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); +#include +#define multipart_log(format, ...) \ + do { \ + fprintf(stderr, "[MULTIPART_PARSER] line %d: " format "\n", __LINE__, __VA_ARGS__); \ + } while (0) +#define multipart_log_c(format) \ + do { \ + if (isprint(c)) { \ + multipart_log("parsing '%c' " format, c); \ + } else { \ + multipart_log("parsing '\\x%0.2x' " format, c); \ + } \ + } while (0) +#else +#define multipart_log(format, ...) +#define multipart_log_c(format, ...) #endif -} - -#define NOTIFY_CB(FOR) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p) != 0) { \ - return i; \ - } \ - } \ -} while (0) - -#define EMIT_DATA_CB(FOR, ptr, len) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p, ptr, len) != 0) { \ - return i; \ - } \ - } \ -} while (0) +#define NOTIFY_CB(FOR, r) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define EMIT_DATA_CB(FOR, r, ptr, len) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p, ptr, len)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define ERROR_OUT(reason) \ + do { \ + p->error_unexpected = c; \ + p->error_i = i; \ + p->error_reason = reason; \ + return MPPE_ERROR; \ + } while (0) + +#define ERROR_EXPECT(reason, ch) \ + do { \ + p->error_expected = ch; \ + if (ch == LF) { \ + multipart_log("expecting LF at %zu, but it's \\x%.2x", i, c); \ + } else if (ch == CR) { \ + multipart_log("expecting CR at %zu, but it's \\x%.2x", i, c); \ + } else { \ + multipart_log("expecting '%c' at %zu, but it's \\x%.2x", ch, i, c); \ + } \ + ERROR_OUT(reason); \ + } while (0) #define LF 10 #define CR 13 enum state { - s_uninitialized = 1, - s_start, - s_start_boundary, - s_header_field_start, - s_header_field, - s_headers_almost_done, - s_header_value_start, - s_header_value, - s_header_value_almost_done, - s_part_data_start, - s_part_data, - s_part_data_almost_boundary, - s_part_data_boundary, - s_part_data_almost_end, - s_part_data_end, - s_part_data_final_hyphen, - s_end + s_uninitialized = 0, + s_start, + s_start_boundary, + s_header_field_start, + s_header_field, + s_headers_almost_done, + s_header_value_start, + s_header_value, + s_header_value_almost_done, + s_part_data_start, + s_part_data, + s_part_data_almost_boundary, + s_part_data_boundary, + s_part_data_almost_end, + s_part_data_end, + s_part_data_final_hyphen, + s_end }; -multipart_parser* multipart_parser_init(const char *boundary, size_t boundary_length, - const multipart_parser_settings* settings) -{ - multipart_parser* p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); - memcpy(p->multipart_boundary, "--", 2); - memcpy(p->multipart_boundary + 2, boundary, boundary_length); - p->multipart_boundary[2 + boundary_length] = 0; - -#if 0 - printf("boundary: %s\r\n\r\n", p->multipart_boundary); -#endif +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings) { + multipart_parser *p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); + memcpy(p->boundary, "--", 2); + memcpy(p->boundary + 2, boundary, boundary_length); + p->boundary[2 + boundary_length] = 0; p->boundary_length = boundary_length + 2; - p->lookbehind = (p->multipart_boundary + p->boundary_length + 1); p->index = 0; p->state = s_start; + p->error_i = 0; + p->error_unexpected = 0; + p->error_expected = 0; + p->error_reason = MPPE_OK; + p->state = s_start; p->settings = settings; return p; } -void multipart_parser_free(multipart_parser* p) { - free(p); +void multipart_parser_free(multipart_parser *p) { + free(p); } -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) { - size_t i = 0; - size_t mark = 0; - char c, cl; - int is_last = 0; - - while(i < len) { - c = buf[i]; - is_last = (i == (len - 1)); - switch (p->state) { - case s_start: - multipart_log("s_start"); - p->index = 0; - p->state = s_start_boundary; - - /* fallthrough */ - case s_start_boundary: - multipart_log("s_start_boundary"); - if (p->index == p->boundary_length) { - if (c != CR) { - return i; - } - p->index++; - break; - } else if (p->index == (p->boundary_length + 1)) { - if (c != LF) { - return i; - } - p->index = 0; - NOTIFY_CB(part_data_begin); - p->state = s_header_field_start; - break; - } - if (c != p->multipart_boundary[p->index]) { - return i; - } - p->index++; +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len) { + int ret; + switch (p->error_reason) { + case MPPE_OK: + return 0; + case MPPE_PAUSED: + return snprintf(buf, len, "parser paused"); + case MPPE_UNKNOWN: + return snprintf(buf, len, "parser unknown"); + default: + return snprintf(buf, len, "parser abort"); + case MPPE_BOUNDARY_END_NO_CRLF: + ret = snprintf(buf, len, "no CRLF at first boundary end: "); break; - - case s_header_field_start: - multipart_log("s_header_field_start"); - mark = i; - p->state = s_header_field; - - /* fallthrough */ - case s_header_field: - multipart_log("s_header_field"); - if (c == CR) { - p->state = s_headers_almost_done; - break; - } - - if (c == '-') { - break; - } - - if (c == ':') { - EMIT_DATA_CB(header_field, buf + mark, i - mark); - p->state = s_header_value_start; - break; - } - - cl = tolower(c); - if (cl < 'a' || cl > 'z') { - multipart_log("invalid character in header name"); - return i; - } - if (is_last) - EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1); + case MPPE_BAD_START_BOUNDARY: + ret = snprintf(buf, len, "first boundary mismatching: "); break; - - case s_headers_almost_done: - multipart_log("s_headers_almost_done"); - if (c != LF) { - return i; - } - - p->state = s_part_data_start; + case MPPE_INVALID_HEADER_FIELD_CHAR: + ret = snprintf(buf, len, "invalid char in header field: "); break; - - case s_header_value_start: - multipart_log("s_header_value_start"); - if (c == ' ') { - break; - } - - mark = i; - p->state = s_header_value; - - /* fallthrough */ - case s_header_value: - multipart_log("s_header_value"); - if (c == CR) { - EMIT_DATA_CB(header_value, buf + mark, i - mark); - p->state = s_header_value_almost_done; - } - if (is_last) - EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1); + case MPPE_INVALID_HEADER_VALUE_CHAR: + ret = snprintf(buf, len, "invalid char in header value: "); break; - - case s_header_value_almost_done: - multipart_log("s_header_value_almost_done"); - if (c != LF) { - return i; - } - p->state = s_header_field_start; + case MPPE_BAD_PART_END: + ret = snprintf(buf, len, "no next part or final hyphen: expecting CR or '-' "); break; - - case s_part_data_start: - multipart_log("s_part_data_start"); - NOTIFY_CB(headers_complete); - mark = i; - p->state = s_part_data; - - /* fallthrough */ - case s_part_data: - multipart_log("s_part_data"); - if (c == CR) { - EMIT_DATA_CB(part_data, buf + mark, i - mark); - mark = i; - p->state = s_part_data_almost_boundary; - p->lookbehind[0] = CR; - break; - } - if (is_last) - EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1); + case MPPE_END_BOUNDARY_NO_DASH: + ret = snprintf(buf, len, "bad final hyphen: "); break; - - case s_part_data_almost_boundary: - multipart_log("s_part_data_almost_boundary"); - if (c == LF) { - p->state = s_part_data_boundary; - p->lookbehind[1] = LF; - p->index = 0; - break; - } - EMIT_DATA_CB(part_data, p->lookbehind, 1); - p->state = s_part_data; - mark = i --; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + switch (p->error_expected) { + case '\0': break; - - case s_part_data_boundary: - multipart_log("s_part_data_boundary"); - if (p->multipart_boundary[p->index] != c) { - EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index); - p->state = s_part_data; - mark = i --; - break; - } - p->lookbehind[2 + p->index] = c; - if ((++ p->index) == p->boundary_length) { - NOTIFY_CB(part_data_end); - p->state = s_part_data_almost_end; - } + case CR: + ret += snprintf(buf + ret, len - ret, "expecting CR "); + break; + case LF: + ret += snprintf(buf + ret, len - ret, "expecting LF "); break; + default: + ret += snprintf(buf + ret, len - ret, "expecting '%c' ", p->error_expected); + break; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + if (isprint(p->error_unexpected)) { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '%c'", p->error_i, p->error_unexpected); + } else { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '\\x%.2x'", p->error_i, p->error_unexpected); + } + return ret; +} - case s_part_data_almost_end: - multipart_log("s_part_data_almost_end"); - if (c == '-') { - p->state = s_part_data_final_hyphen; +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) { + size_t i = 0; + size_t mark = 0; + size_t mark_end = 0; + char c, cl; + int is_last = 0; + int ret; + + while (i < len) { + c = buf[i]; + is_last = (i == (len - 1)); + switch (p->state) { + case s_start: + multipart_log_c("s_start"); + p->index = 0; + p->state = s_start_boundary; + /* fallthrough */ + /* no break */ + case s_start_boundary: + multipart_log_c("s_start_boundary"); + if (p->index == p->boundary_length) { + // https://github.com/swoole/swoole-src/issues/5168 + if (c == '-') { + p->state = s_part_data_final_hyphen; + } else if (c != CR) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, CR); + } + p->index++; + break; + } else if (p->index == (size_t)(p->boundary_length + 1)) { + if (c != LF) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, LF); + } + p->index = 0; + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + if (c != p->boundary[p->index]) { + ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->boundary[p->index]); + } + p->index++; break; - } - if (c == CR) { - p->state = s_part_data_end; + case s_header_field_start: + multipart_log_c("s_header_field_start"); + mark = i; + p->state = s_header_field; + /* fallthrough */ + /* no break */ + case s_header_field: + multipart_log_c("s_header_field"); + if (c == CR) { + p->state = s_headers_almost_done; + break; + } + if (c == '-') { + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); + } + break; + } + if (c == ':') { + p->state = s_header_value_start; + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark); + break; + } + cl = c | 0x20; + if (cl < 'a' || cl > 'z') { + multipart_log_c("invalid character in header field"); + p->error_unexpected = c; + ERROR_OUT(MPPE_INVALID_HEADER_FIELD_CHAR); + } + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); + } break; - } - return i; - - case s_part_data_final_hyphen: - multipart_log("s_part_data_final_hyphen"); - if (c == '-') { - NOTIFY_CB(body_end); - p->state = s_end; + case s_headers_almost_done: + multipart_log_c("s_headers_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } + p->state = s_part_data_start; break; - } - return i; - - case s_part_data_end: - multipart_log("s_part_data_end"); - if (c == LF) { + case s_header_value_start: + multipart_log_c("s_header_value_start"); + if (c == ' ') { + break; + } + mark = i; + p->state = s_header_value; + /* fallthrough */ + /* no break */ + case s_header_value: + multipart_log_c("s_header_value"); + if (c == CR) { + p->state = s_header_value_almost_done; + EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark); + } else if (is_last) { + ERROR_EXPECT(MPPE_HEADER_VALUE_INCOMPLETE, CR); + } + break; + case s_header_value_almost_done: + multipart_log_c("s_header_value_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } p->state = s_header_field_start; - NOTIFY_CB(part_data_begin); break; + case s_part_data_start: + multipart_log_c("s_part_data_start"); + mark = i; + p->state = s_part_data; + NOTIFY_CB(headers_complete, i); + /* fallthrough */ + /* no break */ + case s_part_data: + data_rollback: + multipart_log_c("s_part_data"); + mark_end = i + 1; + if (c == CR) { + if (mark_end - mark - 1 > 0) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark - 1); + } + mark = i; + p->state = s_part_data_almost_boundary; + break; + } + if (is_last) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark); + } + break; + case s_part_data_almost_boundary: + multipart_log_c("s_part_data_almost_boundary"); + if (c != LF) { + EMIT_DATA_CB(part_data, i + 1, "\r", 1); + p->state = s_part_data; + mark = i; + goto data_rollback; + } else { + p->state = s_part_data_boundary; + p->index = 0; + break; + } + case s_part_data_boundary: + multipart_log_c("s_part_data_boundary"); + if (p->boundary[p->index] != c) { + EMIT_DATA_CB(part_data, i + 1, "\r\n", 2); + if (p->index > 0) { + EMIT_DATA_CB(part_data, i + 1, p->boundary, p->index); + } + mark = i; + p->state = s_part_data; + goto data_rollback; + } else { + p->index++; + if (p->index == p->boundary_length) { + p->state = s_part_data_almost_end; + } + break; + } + case s_part_data_almost_end: + multipart_log_c("s_part_data_almost_end"); + if (c == '-') { + p->state = s_part_data_final_hyphen; + NOTIFY_CB(part_data_end, i + 1); + break; + } + if (c == CR) { + p->state = s_part_data_end; + NOTIFY_CB(part_data_end, i + 1); + break; + } + // should be end or another part + multipart_log("expecting '-' or CR at %zu but it's \\x%0.2x", i, c); + ERROR_OUT(MPPE_BAD_PART_END); + case s_part_data_final_hyphen: + multipart_log_c("s_part_data_final_hyphen"); + if (c == '-') { + p->state = s_end; + NOTIFY_CB(body_end, i); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_part_data_end: + multipart_log_c("s_part_data_end"); + if (c == LF) { + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_end: + multipart_log_c("s_end"); + break; + default: + multipart_log_c("Multipart parser unrecoverable error"); + ERROR_OUT(MPPE_UNKNOWN); } - return i; - - case s_end: - multipart_log("s_end: %02X", (int) c); - break; - - default: - multipart_log("Multipart parser unrecoverable error"); - return 0; + ++i; } - ++ i; - } - - return len; + return i; } diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index abf913d255d..3d470b816e6 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -1,15 +1,17 @@ /* Based on node-formidable by Felix Geisendörfer * Igor Afonov - afonov@gmail.com - 2012 * MIT License - http://www.opensource.org/licenses/mit-license.php + * @link https://github.com/libcat/libcat/blob/develop/deps/multipart_parser */ + #ifndef _multipart_parser_h #define _multipart_parser_h #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif +#include #include #include @@ -17,41 +19,94 @@ typedef struct multipart_parser multipart_parser; typedef struct multipart_parser_settings multipart_parser_settings; typedef struct multipart_parser_state multipart_parser_state; -typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length); -typedef int (*multipart_notify_cb) (multipart_parser*); - -struct multipart_parser { - void * data; - void * fp; - - size_t index; - size_t boundary_length; +typedef int (*multipart_data_cb)(multipart_parser *, const char *at, size_t length); +typedef int (*multipart_notify_cb)(multipart_parser *); + +enum multipart_error { + MPPE_OK = 0, + MPPE_PAUSED, + MPPE_UNKNOWN, + MPPE_BOUNDARY_END_NO_CRLF, + MPPE_BAD_START_BOUNDARY, + MPPE_INVALID_HEADER_FIELD_CHAR, + MPPE_INVALID_HEADER_VALUE_CHAR, + MPPE_BAD_PART_END, + MPPE_END_BOUNDARY_NO_DASH, + MPPE_HEADER_VALUE_INCOMPLETE, +}; - unsigned char state; +#define MPPE_ERROR -1 - const multipart_parser_settings* settings; +// from RFC2046 +#define BOUNDARY_MAX_LEN 70 - char* lookbehind; - char multipart_boundary[1]; +struct multipart_parser { + /* private holder for callbacks */ + const multipart_parser_settings *settings; + /* private internal index for matching boundary */ + size_t index; + /* public error unexpected char index */ + size_t error_i; + /* private boundary length + 2 ("--") */ + unsigned char boundary_length; + FILE *fp; + void *data; + /* private FSM state */ + unsigned char state; + /* public error reason */ + unsigned char error_reason; + /* private boundary storage: "--" + boundary */ + char boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9]; + /* public error expected char */ + char error_expected; + /* public error unexpected char */ + char error_unexpected; }; struct multipart_parser_settings { - multipart_data_cb on_header_field; - multipart_data_cb on_header_value; - multipart_data_cb on_part_data; - - multipart_notify_cb on_part_data_begin; - multipart_notify_cb on_headers_complete; - multipart_notify_cb on_part_data_end; - multipart_notify_cb on_body_end; + /* + * data callback called on header field coming + * for example data is "Content-Type" with length 12 + */ + multipart_data_cb on_header_field; + /* + * data callback called on header value coming + * for example data is "plain/text" with length 10 + */ + multipart_data_cb on_header_value; + /* + * data callback called on body data coming, + * will be called repeatedly until data end + */ + multipart_data_cb on_part_data; + /* + * before "--" boundary + */ + multipart_notify_cb on_part_data_begin; + /* + * after all headers line "\r\n", before body + */ + multipart_notify_cb on_headers_complete; + /* + * after body, before next "--" boundary + */ + multipart_notify_cb on_part_data_end; + /* + * after last "--" boundary "--" + */ + multipart_notify_cb on_body_end; }; -multipart_parser* multipart_parser_init - (const char *boundary, size_t boundary_length, const multipart_parser_settings* settings); +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings); +void multipart_parser_free(multipart_parser *p); -void multipart_parser_free(multipart_parser* p); - -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len); +/** + * The multipart header must be complete, otherwise it will be parsed incorrectly + */ +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len); #ifdef __cplusplus } /* extern "C" */ diff --git a/thirdparty/nghttp2/LICENSE b/thirdparty/nghttp2/LICENSE new file mode 100644 index 00000000000..d9e4eb197b9 --- /dev/null +++ b/thirdparty/nghttp2/LICENSE @@ -0,0 +1 @@ +See COPYING diff --git a/thirdparty/nghttp2/nghttp2.h b/thirdparty/nghttp2/nghttp2.h index cf099d2e0c8..29f12ac79fa 100644 --- a/thirdparty/nghttp2/nghttp2.h +++ b/thirdparty/nghttp2/nghttp2.h @@ -52,28 +52,27 @@ extern "C" { #include #include #include +#include +const char *nghttp2_strerror(int error_code); static inline uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { - if (len == 0) { - return dest; - } + if (len == 0) { + return dest; + } - memcpy(dest, src, len); + memcpy(dest, src, len); - return dest + len; + return dest + len; } -#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) -#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) - #define DEBUGF(s, ...) - -const char *nghttp2_strerror(int error_code); +#define nghttp2_min_size(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max_size(A, B) ((A) > (B) ? (A) : (B)) #ifdef NGHTTP2_STATICLIB # define NGHTTP2_EXTERN -#elif defined(WIN32) || (__has_declspec_attribute(dllexport) && \ - __has_declspec_attribute(dllimport)) +#elif defined(WIN32) || \ + (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport)) # ifdef BUILDING_NGHTTP2 # define NGHTTP2_EXTERN __declspec(dllexport) # else /* !BUILDING_NGHTTP2 */ @@ -87,6 +86,17 @@ const char *nghttp2_strerror(int error_code); # endif /* !BUILDING_NGHTTP2 */ #endif /* !defined(WIN32) */ +#ifdef BUILDING_NGHTTP2 +# undef NGHTTP2_NO_SSIZE_T +#endif /* BUILDING_NGHTTP2 */ + +/** + * @typedef + * + * :type:`nghttp2_ssize` is a signed counterpart of size_t. + */ +typedef ptrdiff_t nghttp2_ssize; + /** * @macro * @@ -135,6 +145,15 @@ const char *nghttp2_strerror(int error_code); */ #define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; /** * @macro @@ -175,6 +194,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The default weight of stream dependency. */ #define NGHTTP2_DEFAULT_WEIGHT 16 @@ -182,6 +207,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The maximum weight of stream dependency. */ #define NGHTTP2_MAX_WEIGHT 256 @@ -189,6 +220,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The minimum weight of stream dependency. */ #define NGHTTP2_MIN_WEIGHT 1 @@ -262,7 +299,7 @@ typedef enum { */ NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, /** - * Used as a return value from :type:`nghttp2_send_callback`, + * Used as a return value from :type:`nghttp2_send_callback2`, * :type:`nghttp2_recv_callback` and * :type:`nghttp2_send_data_callback` to indicate that the operation * would block. @@ -282,9 +319,9 @@ typedef enum { NGHTTP2_ERR_EOF = -507, /** * Used as a return value from - * :func:`nghttp2_data_source_read_callback` to indicate that data + * :func:`nghttp2_data_source_read_callback2` to indicate that data * transfer is postponed. See - * :func:`nghttp2_data_source_read_callback` for details. + * :func:`nghttp2_data_source_read_callback2` for details. */ NGHTTP2_ERR_DEFERRED = -508, /** @@ -447,7 +484,12 @@ typedef enum { * exhaustion on server side to send these frames forever and does * not read network. */ - NGHTTP2_ERR_FLOODED = -904 + NGHTTP2_ERR_FLOODED = -904, + /** + * When a local endpoint receives too many CONTINUATION frames + * following a HEADER frame. + */ + NGHTTP2_ERR_TOO_MANY_CONTINUATIONS = -905, } nghttp2_error; /** @@ -499,6 +541,15 @@ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); */ NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); +/** + * @function + * + * Returns nonzero if the underlying buffer is statically allocated, + * and 0 otherwise. This can be useful for language bindings that wish + * to avoid creating duplicate strings for these buffers. + */ +NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); + /** * @enum * @@ -632,7 +683,11 @@ typedef enum { * The ORIGIN frame, which is defined by `RFC 8336 * `_. */ - NGHTTP2_ORIGIN = 0x0c + NGHTTP2_ORIGIN = 0x0c, + /** + * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. + */ + NGHTTP2_PRIORITY_UPDATE = 0x10 } nghttp2_frame_type; /** @@ -701,7 +756,11 @@ typedef enum { * SETTINGS_ENABLE_CONNECT_PROTOCOL * (`RFC 8441 `_) */ - NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, + /** + * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) + */ + NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ @@ -820,7 +879,7 @@ typedef struct { * @union * * This union represents the some kind of data source passed to - * :type:`nghttp2_data_source_read_callback`. + * :type:`nghttp2_data_source_read_callback2`. */ typedef union { /** @@ -837,7 +896,7 @@ typedef union { * @enum * * The flags used to set in |data_flags| output parameter in - * :type:`nghttp2_data_source_read_callback`. + * :type:`nghttp2_data_source_read_callback2`. */ typedef enum { /** @@ -851,8 +910,8 @@ typedef enum { /** * Indicates that END_STREAM flag must not be set even if * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send - * trailer fields with `nghttp2_submit_request()` or - * `nghttp2_submit_response()`. + * trailer fields with `nghttp2_submit_request2()` or + * `nghttp2_submit_response2()`. */ NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, /** @@ -862,61 +921,188 @@ typedef enum { NGHTTP2_DATA_FLAG_NO_COPY = 0x04 } nghttp2_data_flag; +#ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef + * .. warning:: * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp2_mem` structure. + * Deprecated. Use :type:`nghttp2_data_source_read_callback2` + * instead. + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag + * in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to + * |*data_flags| (and possibly other flags, just like when we do + * copy), and return the number of bytes to send without copying data + * into |buf|. The library, seeing + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to + * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, + * this is expected behaviour and all are fine. One exception is send + * trailer fields. You cannot send trailer fields after sending frame + * with END_STREAM set. To avoid this problem, one can set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along + * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the + * library not to set END_STREAM in DATA frame. Then application can + * use `nghttp2_submit_trailer()` to send trailer fields. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` + * without reading any data in this invocation. The library removes + * DATA frame from the outgoing queue temporarily. To move back + * deferred DATA frame to outgoing queue, call + * `nghttp2_session_resume_data()`. + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without + * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. + * + * In case of error, there are 2 choices. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream by issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different + * error code is desirable, use `nghttp2_submit_rst_stream()` with a + * desired error code and then return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will + * signal the entire session failure. */ -typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag + * in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to + * |*data_flags| (and possibly other flags, just like when we do + * copy), and return the number of bytes to send without copying data + * into |buf|. The library, seeing + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request2()`, + * `nghttp2_submit_response2()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data2()` with flag parameter + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to + * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, + * this is expected behaviour and all are fine. One exception is send + * trailer fields. You cannot send trailer fields after sending frame + * with END_STREAM set. To avoid this problem, one can set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along + * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the + * library not to set END_STREAM in DATA frame. Then application can + * use `nghttp2_submit_trailer()` to send trailer fields. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` + * without reading any data in this invocation. The library removes + * DATA frame from the outgoing queue temporarily. To move back + * deferred DATA frame to outgoing queue, call + * `nghttp2_session_resume_data()`. + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send2()` without + * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. + * + * In case of error, there are 2 choices. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream by issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different + * error code is desirable, use `nghttp2_submit_rst_stream()` with a + * desired error code and then return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will + * signal the entire session failure. */ -typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef nghttp2_ssize (*nghttp2_data_source_read_callback2)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); +#ifndef NGHTTP2_NO_SSIZE_T /** - * @functypedef + * @struct * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_data_provider2` instead. + * + * This struct represents the data source and the way to read a chunk + * of data from it. */ -typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); - typedef struct { /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). + * The data source. */ - nghttp2_malloc malloc; + nghttp2_data_source source; /** - * Custom allocator function to replace free(). + * The callback function to read a chunk of data from the |source|. */ - nghttp2_free free; + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { /** - * Custom allocator function to replace calloc(). + * The data source. */ - nghttp2_calloc calloc; + nghttp2_data_source source; /** - * Custom allocator function to replace realloc(). + * The callback function to read a chunk of data from the |source|. */ - nghttp2_realloc realloc; -} nghttp2_mem; + nghttp2_data_source_read_callback2 read_callback; +} nghttp2_data_provider2; /** * @struct @@ -970,6 +1156,12 @@ typedef enum { /** * @struct * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The structure to specify stream dependency. */ typedef struct { @@ -1004,6 +1196,12 @@ typedef struct { */ size_t padlen; /** + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The priority specification */ nghttp2_priority_spec pri_spec; @@ -1024,6 +1222,12 @@ typedef struct { /** * @struct * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The PRIORITY frame. It has the following members: */ typedef struct { @@ -1267,182 +1471,4887 @@ typedef union { nghttp2_extension ext; } nghttp2_frame; -/* HPACK API */ - -struct nghttp2_hd_deflater; - +#ifndef NGHTTP2_NO_SSIZE_T /** - * @struct + * @functypedef * - * HPACK deflater object. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_send_callback2` instead. + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For + * other errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + * + * .. note:: + * + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. */ -typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** - * @function - * - * Initializes |*deflater_ptr| for deflating name/values pairs. - * - * The |max_deflate_dynamic_table_size| is the upper bound of header - * table size the deflater will use. + * @functypedef * - * If this function fails, |*deflater_ptr| is left untouched. + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For + * other errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send2()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback2()`. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * .. note:: * - * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size); +typedef nghttp2_ssize (*nghttp2_send_callback2)(nghttp2_session *session, + const uint8_t *data, + size_t length, int flags, + void *user_data); /** - * @function + * @functypedef * - * Like `nghttp2_hd_deflate_new()`, but with additional custom memory - * allocator specified in the |mem|. + * Callback function invoked when + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to send complete DATA + * frame. * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_deflate_new()`. + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->data.padlen > 0``, send 1 byte of value + * ``frame->data.padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->data.padlen > 1``, send + * ``frame->data.padlen - 1`` bytes of zero as padding. + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send any data at all, just return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call + * this callback with the same parameters later (It is recommended to + * send complete DATA frame at once in this function to deal with + * error; if partial frame data has already sent, it is impossible to + * send another data in that state, and all we can do is tear down + * connection). When data is fully processed, but application wants + * to make `nghttp2_session_mem_send2()` or `nghttp2_session_send()` + * return immediately without processing next frames, return + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. If application decided to + * reset this stream, return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then + * the library will send RST_STREAM with INTERNAL_ERROR as error code. + * The application can also return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will + * result in connection closure. Returning any other value is treated + * as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_recv_callback2` instead. + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF + * before it reads any single byte, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must + * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| + * pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size, - nghttp2_mem *mem); +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** - * @function + * @functypedef * - * Deallocates any resources allocated for |deflater|. + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF + * before it reads any single byte, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must + * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| + * pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv2()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback2()`. */ -NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); +typedef nghttp2_ssize (*nghttp2_recv_callback2)(nghttp2_session *session, + uint8_t *buf, size_t length, + int flags, void *user_data); /** - * @function + * @functypedef * - * Changes header table size of the |deflater| to - * |settings_max_dynamic_table_size| bytes. This may trigger eviction - * in the dynamic table. + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * - * The |settings_max_dynamic_table_size| should be the value received - * in SETTINGS_HEADER_TABLE_SIZE. + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. * - * The deflater never uses more memory than - * ``max_deflate_dynamic_table_size`` bytes specified in - * `nghttp2_hd_deflate_new()`. Therefore, if - * |settings_max_dynamic_table_size| > - * ``max_deflate_dynamic_table_size``, resulting maximum table size - * becomes ``max_deflate_dynamic_table_size``. + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. * - * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_max_dynamic_table_size); +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function + * @functypedef * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |buf| of length |buflen|. + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef * - * If |buf| is not large enough to store the deflated header block, - * this function fails with - * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller - * should use `nghttp2_hd_deflate_bound()` to know the upper bound of - * buffer size required to deflate given header name/value pairs. + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv2()`, it can + * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make + * `nghttp2_session_mem_recv2()` return without processing further + * input bytes. The memory by pointed by the |data| is retained until + * `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is + * called. The application must retain the input bytes which was used + * to produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef * - * Once this function fails, subsequent call of this function always - * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to + * cancel the transmission of the given frame. + * + * If there is a fatal error while executing this callback, the + * implementation should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * If the other value is returned, it is treated as if + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + * But the implementation should not rely on this since the library + * may define new return value to extend its capability. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef * - * After this function returns, it is safe to delete the |nva|. + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * - * This function returns the number of bytes written to |buf| if it - * succeeds, or one of the following negative error codes: + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * - * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. - * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t *buf, size_t buflen, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function + * @functypedef * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |veclen| size of buf vector |vec|. The each size of buffer - * must be set in len field of :type:`nghttp2_vec`. If and only if - * one chunk is filled up completely, next chunk will be used. If - * |vec| is not large enough to store the deflated header block, this - * function fails with - * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller - * should use `nghttp2_hd_deflate_bound()` to know the upper bound of - * buffer size required to deflate given header name/value pairs. + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. * - * Once this function fails, subsequent call of this function always - * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * - * After this function returns, it is safe to delete the |nva|. + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. * - * This function returns the number of bytes written to |vec| if it - * succeeds, or one of the following negative error codes: + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. + */ +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); + +/** + * @functypedef * - * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. - * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request2()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv2()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, - const nghttp2_vec *vec, - size_t veclen, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); /** - * @function + * @functypedef * - * Returns an upper bound on the compressed size after deflation of - * |nva| of length |nvlen|. + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which + * indicates that one or more CONTINUATION frames are involved. But + * the application does not need to care about that because the header + * name/value pairs are emitted transparently regardless of + * CONTINUATION frames. + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer fields and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer fields also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * + * Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream (promised stream if frame is PUSH_PROMISE) by + * issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Again, use ``frame->push_promise.promised_stream_id`` as stream_id + * parameter in `nghttp2_submit_rst_stream()` if frame is + * PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * value is returned, it is treated as if + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_mem_recv2()` function will immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. */ -NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function + * @functypedef * - * Returns the number of entries that header table of |deflater| - * contains. This is the sum of the number of static table and - * dynamic table, so the return value is at least 61. + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * + * If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in + * |flags|, the receiver must not index this name/value pair when + * forwarding it to the next hop. More specifically, "Literal Header + * Field never Indexed" representation must be used in HPACK encoding. + * + * When this callback is invoked, ``frame->hd.type`` is either + * :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or + * :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`. After all header + * name/value pairs are processed with this callback, and no error has + * been detected, :type:`nghttp2_on_frame_recv_callback` will be + * invoked. If there is an error in decompression, + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs validation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv2()`, it can + * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make + * `nghttp2_session_mem_recv2()` return without processing further + * input bytes. The memory pointed by |frame|, |name| and |value| + * parameters are retained until `nghttp2_session_mem_recv2()` or + * `nghttp2_session_recv()` is called. The application must retain + * the input bytes which was used to produce these parameters, because + * it may refer to the memory region included in the input bytes. + * + * Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream (promised stream if frame is PUSH_PROMISE) by + * issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Again, use ``frame->push_promise.promised_stream_id`` as stream_id + * parameter in `nghttp2_submit_rst_stream()` if frame is + * PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For + * other critical failures, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * nonzero value is returned, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + * + * .. warning:: + * + * Application should properly limit the total buffer size to store + * incoming header fields. Without it, peer may send large number + * of header fields or large header fields to cause out of memory in + * local endpoint. Due to how HPACK works, peer can do this + * effectively without using much memory on their own. */ -NGHTTP2_EXTERN -size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); /** - * @function + * @functypedef * - * Returns the table entry denoted by |idx| from header table of - * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| is header name. The |value| is header + * value. The |flags| is bitwise OR of one or more of + * :type:`nghttp2_nv_flag`. + * + * This callback behaves like :type:`nghttp2_on_header_callback`, + * except that |name| and |value| are stored in reference counted + * buffer. If application wishes to keep these references without + * copying them, use `nghttp2_rcbuf_incref()` to increment their + * reference count. It is the application's responsibility to call + * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so + * as not to leak memory. If the |session| is created by + * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, + * the function to free memory is the one belongs to the mem + * parameter. As long as this free function alives, |name| and + * |value| can live after |session| was destroyed. + */ +typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, + const nghttp2_frame *frame, + nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is treated as stream error if this callback is not + * set. Only invalid regular header field are passed to this + * callback. In other words, invalid pseudo header field is not + * passed to this callback. Also header fields which includes upper + * cased latter are also treated as error without passing them to this + * callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By + * default, the error code is + * :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`. To change the + * error code, call `nghttp2_submit_rst_stream()` with the error code + * of choice in addition to returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * + * If 0 is returned, the header field is ignored, and the stream is + * not reset. + */ +typedef int (*nghttp2_on_invalid_header_callback)( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback2`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is silently ignored if this callback is not set. + * Only invalid regular header field are passed to this callback. In + * other words, invalid pseudo header field is not passed to this + * callback. Also header fields which includes upper cased latter are + * also treated as error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By + * default, the error code is + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. To change the + * error code, call `nghttp2_submit_rst_stream()` with the error code + * of choice in addition to returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_invalid_header_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_select_padding_callback2` + * instead. + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback2()`. + */ +typedef nghttp2_ssize (*nghttp2_select_padding_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use + * :type:`nghttp2_data_source_read_length_callback2` instead. + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |frame_type| is provided for future extensibility and identifies + * the type of frame (see :type:`nghttp2_frame_type`) for which to get + * the length for. Currently supported frame types are: + * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the + * entire session failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |frame_type| is provided for future extensibility and identifies + * the type of frame (see :type:`nghttp2_frame_type`) for which to get + * the length for. Currently supported frame types are: + * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the + * entire session failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback2()`. + */ +typedef nghttp2_ssize (*nghttp2_data_source_read_length_callback2)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when chunk of extension frame payload is + * received. The |hd| points to frame header. The received + * chunk is |data| of length |len|. + * + * The implementation of this function must return 0 if it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_extension_chunk_recv_callback)( + nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to + * unpack extension payload from its wire format. The extension + * payload has been passed to the application using + * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header + * is already unpacked by the library and provided as |hd|. + * + * To receive extension frames, the application must tell desired + * extension frame type to the library using + * `nghttp2_option_set_user_recv_extension_type()`. + * + * The implementation of this function may store the pointer to the + * created object as a result of unpacking in |*payload|, and returns + * 0. The pointer stored in |*payload| is opaque to the library, and + * the library does not own its pointer. |*payload| is initialized as + * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in + * :type:`nghttp2_on_frame_recv_callback`. Therefore if application + * can free that memory inside :type:`nghttp2_on_frame_recv_callback` + * callback. Of course, application has a liberty not to use + * |*payload|, and do its own mechanism to process extension frames. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, + void **payload, + const nghttp2_frame_hd *hd, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_pack_extension_callback2` + * instead. + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return + * value is strictly larger than |len|, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, + uint8_t *buf, size_t len, + const nghttp2_frame *frame, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return + * value is strictly larger than |len|, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef nghttp2_ssize (*nghttp2_pack_extension_callback2)( + nghttp2_session *session, uint8_t *buf, size_t len, + const nghttp2_frame *frame, void *user_data); + +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_error_callback2` instead. + * + * Callback function invoked when library provides the error message + * intended for human consumption. This callback is solely for + * debugging purpose. The |msg| is typically NULL-terminated string + * of length |len|. |len| does not include the sentinel NULL + * character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * In this case, library will return immediately with return value + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if + * nonzero value is returned from this callback, they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application + * should not rely on this details. + */ +typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error code, and + * message. This callback is solely for debugging purpose. + * |lib_error_code| is one of error code defined in + * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated + * string of length |len|, and intended for human consumption. |len| + * does not include the sentinel NULL character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * In this case, library will return immediately with return value + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if + * nonzero value is returned from this callback, they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application + * should not rely on this details. + */ +typedef int (*nghttp2_error_callback2)(nghttp2_session *session, + int lib_error_code, const char *msg, + size_t len, void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_callbacks_set_send_callback2()` + * with :type:`nghttp2_send_callback2` instead. + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send2()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback2( + nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_callbacks_set_recv_callback2()` + * with :type:`nghttp2_recv_callback2` instead. + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv2()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback2( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. If both + * `nghttp2_session_callbacks_set_on_header_callback()` and + * `nghttp2_session_callbacks_set_on_header_callback2()` are used to + * set callbacks, the latter has the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. If both + * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and + * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are + * used to set callbacks, the latter takes the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_select_padding_callback2()` with + * :type:`nghttp2_select_padding_callback2` instead. + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback2 select_padding_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_data_source_read_length_callback2()` + * with :type:`nghttp2_data_source_read_length_callback2` instead. + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback2`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback2 data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback2` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_pack_extension_callback2()` with + * :type:`nghttp2_pack_extension_callback2` instead. + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback2 pack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to unpack extension frame payload from wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when chunk of extension frame + * payload is received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_error_callback2()` with + * :type:`nghttp2_error_callback2` instead. + * + * Sets callback function invoked when library tells error message to + * the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error code, and + * message to the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, the maximum number of outgoing concurrent + * streams is initially limited to 100 to avoid issues when the local + * endpoint submits lots of requests before receiving initial SETTINGS + * frame from the remote endpoint, since sending them at once to the + * remote endpoint could lead to rejection of some of the requests. + * This value will be overwritten when the local endpoint receives + * initial SETTINGS frame from the remote endpoint, either to the + * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the + * default value (unlimited) if none was advertised. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv2()` will return error + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal + * error. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * `_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. Please note that disabling this feature + * does not change the fundamental client and server model of HTTP. + * That is, even if the validation is disabled, only client can send + * requests. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * RFC 7540 does not enforce any limit on the number of incoming + * reserved streams (in RFC 7540 terms, streams in reserved (remote) + * state). This only affects client side, since only server can push + * streams. Malicious server can push arbitrary number of streams, + * and make client's memory exhausted. This option can set the + * maximum number of such incoming streams to avoid possible memory + * exhaustion. If this option is set, and pushed streams are + * automatically closed on reception, without calling user provided + * callback, if they exceed the given limit. The default value is + * 200. If session is configured as server side, this option has no + * effect. Server can control the number of streams to push. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * Sets extension frame type the application is willing to handle with + * user defined callbacks (see + * :type:`nghttp2_on_extension_chunk_recv_callback` and + * :type:`nghttp2_unpack_extension_callback`). The |type| is + * extension frame type, and must be strictly greater than 0x9. + * Otherwise, this function does nothing. The application can call + * this function multiple times to set more than one frame type to + * receive. The application does not have to call this function if it + * just sends extension frames. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * Sets extension frame type the application is willing to receive + * using builtin handler. The |type| is the extension frame type to + * receive, and must be strictly greater than 0x9. Otherwise, this + * function does nothing. The application can call this function + * multiple times to set more than one frame type to receive. The + * application does not have to call this function if it just sends + * extension frames. + * + * If same frame type is passed to both + * `nghttp2_option_set_builtin_recv_extension_type()` and + * `nghttp2_option_set_user_recv_extension_type()`, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * This option prevents the library from sending PING frame with ACK + * flag set automatically when PING frame without ACK flag set is + * received. If this option is set to nonzero, the library won't send + * PING frame with ACK flag set in the response for incoming PING + * frame. The application can send PING frame with ACK flag set using + * `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * as flags parameter. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, + int val); + +/** + * @function + * + * This option sets the maximum length of header block (a set of + * header fields per one HEADERS frame) to send. The length of a + * given set of header fields is calculated using + * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If + * application attempts to send header fields larger than this limit, + * the transmission of the frame fails with error code + * :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option sets the maximum dynamic table size for deflating + * header fields. The default value is 4KiB. In HTTP/2, receiver of + * deflated header block can specify maximum dynamic table size. The + * actual maximum size is the minimum of the size receiver specified + * and this option value. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option prevents the library from retaining closed streams to + * maintain the priority tree. If this option is set to nonzero, + * applications can discard closed stream completely to save memory. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, any + * closed streams are not retained regardless of this option. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); + +/** + * @function + * + * This function sets the maximum number of outgoing SETTINGS ACK and + * PING ACK frames retained in :type:`nghttp2_session` object. If + * more than those frames are retained, the peer is considered to be + * misbehaving and session will be closed. The default value is 1000. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This function sets the maximum number of SETTINGS entries per + * SETTINGS frame that will be accepted. If more than those entries + * are received, the peer is considered to be misbehaving and session + * will be closed. The default value is 32. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option, if set to nonzero, allows server to fallback to + * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not + * received from client, and server submitted + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * = 1 via `nghttp2_submit_settings()`. Most of the advanced + * functionality for RFC 7540 priorities are still disabled. This + * fallback only enables the minimal feature set of RFC 7540 + * priorities to deal with priority signaling from client. + * + * Client session ignores this option. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, + int val); + +/** + * @function + * + * This option, if set to nonzero, turns off RFC 9113 leading and + * trailing white spaces validation against HTTP field value. Some + * important fields, such as HTTP/2 pseudo header fields, are + * validated more strictly and this option does not apply to them. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( + nghttp2_option *option, int val); + +/** + * @function + * + * This function sets the rate limit for the incoming stream reset + * (RST_STREAM frame). It is server use only. It is a token-bucket + * based rate limiter. |burst| specifies the number of tokens that is + * initially available. The maximum number of tokens is capped to + * this value. |rate| specifies the number of tokens that are + * regenerated per second. An incoming RST_STREAM consumes one token. + * If there is no token available, GOAWAY is sent to tear down the + * connection. |burst| and |rate| default to 1000 and 33 + * respectively. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, + uint64_t burst, uint64_t rate); + +/** + * @function + * + * This function sets the maximum number of CONTINUATION frames + * following an incoming HEADER frame. If more than those frames are + * received, the remote endpoint is considered to be misbehaving and + * session will be closed. The default value is 8. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option, + size_t val); + +/** + * @function + * + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback2` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback2` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many times as possible until the user callback + * :type:`nghttp2_send_callback2` returns + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue + * becomes empty or flow control is triggered (remote window size + * becomes depleted or maximum number of concurrent streams is + * reached). This function calls several callback functions which are + * passed when initializing the |session|. Here is the simple time + * chart which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * + * 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from + * :type:`nghttp2_before_frame_send_callback`, the current frame + * transmission is canceled, and + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 8. :type:`nghttp2_send_callback2` is invoked one or more times to + * send the frame. + * + * 9. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 10. If the transmission of the frame triggers closure of the + * stream, the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_mem_send2()` instead. + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback2` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send2()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN nghttp2_ssize +nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. This function calls + * several callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. For invalid header field, + * :type:`nghttp2_on_invalid_header_callback` is called. After + * all name/value pairs are emitted successfully, + * :type:`nghttp2_on_frame_recv_callback` is invoked. For other + * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. + * If the reception of the frame triggers the closure of the + * stream, :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_mem_recv2()` instead. + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes to receive in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes |inlen| bytes of input data unless either an error occurs or + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value + * includes the number of bytes which was used to produce the data or + * frame for the callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes to receive in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes |inlen| bytes of input data unless either an error occurs or + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value + * includes the number of bytes which was used to produce the data or + * frame for the callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist; or no deferred data exist. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request2()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Sets |user_data| to |session|, overwriting the existing user data + * specified in `nghttp2_session_client_new()`, or + * `nghttp2_session_server_new()`. + */ +NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, + void *user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_stream_local_window_size()` to know the amount + * of data the remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * connection level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. It is also subject to the connection level + * flow control. So the actual amount of data to send is + * min(`nghttp2_session_get_stream_local_window_size()`, + * `nghttp2_session_get_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_local_window_size()` to know the amount of + * data the remote endpoint can send without receiving + * connection-level WINDOW_UPDATE frame. Note that each stream is + * still subject to the stream level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving connection level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * stream level flow control (see + * `nghttp2_session_get_stream_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the current dynamic table size of HPACK inflater, including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Returns the current dynamic table size of HPACK deflater including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Returns the value of SETTINGS |id| of local endpoint acknowledged + * by the remote endpoint. The |id| must be one of the values defined + * in :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t +nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Changes priority of existing stream denoted by |stream_id|. The + * new priority specification is |pri_spec|. + * + * The priority is changed silently and instantly, and no PRIORITY + * frame will be sent to notify the peer of this change. This + * function may be useful for server to change the priority of pushed + * stream. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or no stream exist for the given + * |stream_id|; or |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_change_stream_priority(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Creates idle stream with the given |stream_id|, and priority + * |pri_spec|. + * + * The stream creation is done without sending PRIORITY frame, which + * means that peer does not know about the existence of this idle + * stream in the local endpoint. + * + * RFC 7540 does not disallow the use of creation of idle stream with + * odd or even stream ID regardless of client or server. So this + * function can create odd or even stream ID regardless of client or + * server. But probably it is a bit safer to use the stream ID the + * local endpoint can initiate (in other words, use odd stream ID for + * client, and even stream ID for server), to avoid potential + * collision from peer's instruction. Also we can use + * `nghttp2_session_set_next_stream_id()` to avoid to open created + * idle streams accidentally if we follow this recommendation. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or stream denoted by |stream_id| + * already exists; or |stream_id| cannot be used to create idle + * stream (in other words, local endpoint has already opened + * stream ID greater than or equal to the given stream ID; or + * |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * This function is deprecated in favor of + * `nghttp2_session_upgrade2()`, because this function lacks the + * parameter to tell the library the request method used in the + * original HTTP request. This information is required for client + * to validate actual response body length against content-length + * header field (see `nghttp2_option_set_no_http_messaging()`). If + * HEAD is used in request, the length of response body must be 0 + * regardless of value included in content-length header field. + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * If the request method is HEAD, pass nonzero value to + * |head_request|. Otherwise, pass 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + int head_request, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_pack_settings_payload2()` instead. + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_pack_settings_payload2( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Returns string representation of HTTP/2 error code |error_code| + * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == + * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for + * given |error_code|, this function returns string ``unknown``. + */ +NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * + * The |weight| must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_request2()` instead. + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not created yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, + void *stream_user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. In the future release + * after the end of 2024, this function will ignore |pri_spec| and + * behave as if ``NULL`` is given. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not created yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request2( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_response2()` instead. + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response()` twice, + * this may happen. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response2()` twice, + * this may happen. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider2 *data_prd); + +/** + * @function + * + * Submits trailer fields HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application must not include pseudo-header + * fields (headers whose names starts with ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * For server, trailer fields must follow response HEADERS or response + * DATA without END_STREAM flat set. The library does not enforce + * this requirement, and applications should do this for themselves. + * If `nghttp2_submit_trailer()` is called before any response HEADERS + * submission (usually by `nghttp2_submit_response2()`), the content + * of |nva| will be sent as response headers, which will result in + * error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both + * pri_spec and stream_user_data to NULL. + * + * To submit trailer fields after `nghttp2_submit_response2()` is + * called, the application has to specify + * :type:`nghttp2_data_provider2` to `nghttp2_submit_response2()`. + * Inside of :type:`nghttp2_data_source_read_callback2`, when setting + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`. After + * that, the application can send trailer fields using + * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used + * inside :type:`nghttp2_data_source_read_callback2`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` + * + * If |flags| includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, + * this frame has END_STREAM flag set. + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. In the future release + * after the end of 2024, this function will ignore |pri_spec| and + * behave as if ``NULL`` is given. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request2()` is useful. Likewise, for HTTP + * response, prefer `nghttp2_submit_response2()`. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or trying to depend on itself (stream ID + * equals ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. This happens if stream denoted by |stream_id| + * is in reserved state. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |stream_id| is -1, and |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_headers( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_data2()` instead. + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame + * has END_STREAM flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA + * or HEADERS is finished results in + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous frame is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data()`. Of + * course, all data except for last one must not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + * This sounds a bit complicated, and we recommend to use + * `nghttp2_submit_request()` and `nghttp2_submit_response()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame + * has END_STREAM flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA + * or HEADERS is finished results in + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous frame is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data2()`. Of + * course, all data except for last one must not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + * This sounds a bit complicated, and we recommend to use + * `nghttp2_submit_request2()` and `nghttp2_submit_response2()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider2 *data_prd); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` is not allowed for this function. To specify the + * priority, use `nghttp2_priority_spec_init()`. This function will + * copy its data members. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, this function does + * nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency + * level for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level + * for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_HIGH 0 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for + * :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LOW 7 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency + * levels for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1) + +/** + * @struct + * + * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities + * specification for a stream. + */ +typedef struct nghttp2_extpri { + /** + * :member:`urgency` is the urgency of a stream, it must be in + * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the + * highest urgency. + */ + uint32_t urgency; + /** + * :member:`inc` indicates that a content can be processed + * incrementally or not. If inc is 0, it cannot be processed + * incrementally. If inc is 1, it can be processed incrementally. + * Other value is not permitted. + */ + int inc; +} nghttp2_extpri; + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is + * automatically submitted by the library and application could not + * send it at its will. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response2()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * This function was invoked when |session| is initialized as + * client. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * As of 1.16.0, stream object for pushed resource is created when + * this function succeeds. In that case, the application can submit + * push response for the promised frame. + * + * In 1.15.0 or prior versions, pushed stream is not opened yet when + * this function succeeds. The application must not submit frame to + * that stream ID before :type:`nghttp2_before_frame_send_callback` + * is called for this frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is bitwise OR of 0 or more of the following value. + * + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * + * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| + * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if new request can be sent from local endpoint. + * + * This function return 0 if request is not allowed for this session. + * There are several reasons why request is not allowed. Some of the + * reasons are: session is server; stream ID has been spent; GOAWAY + * has been sent or received. + * + * The application can call `nghttp2_submit_request2()` without + * consulting this function. In that case, + * `nghttp2_submit_request2()` may return error. Or, request is + * failed to sent, and :type:`nghttp2_on_stream_close_callback` is + * called. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_request_allowed(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if |session| is initialized as server side session. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_server_session(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. If the sole purpose is to increase the local window + * size, consider to use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. If + * the sole purpose is to decrease the local window size, consider to + * use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is 0, the function does nothing and + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOW_CONTROL` + * The local window size overflow or gets negative. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Set local window size (local endpoints's window size) to the given + * |window_size| for the given stream denoted by |stream_id|. To + * change connection level window size, specify 0 to |stream_id|. To + * increase window size, this function may submit WINDOW_UPDATE frame + * to transmission queue. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This sounds similar to `nghttp2_submit_window_update()`, but there + * are 2 differences. The first difference is that this function + * takes the absolute value of window size to set, rather than the + * delta. To change the window size, this may be easier to use since + * the application just declares the intended window size, rather than + * calculating delta. The second difference is that + * `nghttp2_submit_window_update()` affects the received bytes count + * which has not acked yet. By the specification of + * `nghttp2_submit_window_update()`, to strictly increase the local + * window size, we have to submit delta including all received bytes + * count, which might not be desirable in some cases. On the other + * hand, this function does not affect the received bytes count. It + * just sets the local window size to the given value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is negative. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, + int32_t stream_id, int32_t window_size); + +/** + * @function + * + * Submits extension frame. + * + * Application can pass arbitrary frame flags and stream ID in |flags| + * and |stream_id| respectively. The |payload| is opaque pointer, and + * it can be accessible though ``frame->ext.payload`` in + * :type:`nghttp2_pack_extension_callback2`. The library will not own + * passed |payload| pointer. + * + * The application must set :type:`nghttp2_pack_extension_callback2` + * using `nghttp2_session_callbacks_set_pack_extension_callback2()`. + * + * The application should retain the memory pointed by |payload| until + * the transmission of extension frame is done (which is indicated by + * :type:`nghttp2_on_frame_send_callback`), or transmission fails + * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). + * If application does not touch this memory region after packing it + * into a wire format, application can free it inside + * :type:`nghttp2_pack_extension_callback2`. + * + * The standard HTTP/2 frame cannot be sent with this function, so + * |type| must be strictly grater than 0x9. Otherwise, this function + * will fail with error code + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * If :type:`nghttp2_pack_extension_callback2` is not set. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * If |type| specifies standard HTTP/2 frame type. The frame + * types in the rage [0x0, 0x9], both inclusive, are standard + * HTTP/2 frame type, and cannot be sent using this function. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + */ +NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, + uint8_t type, uint8_t flags, + int32_t stream_id, void *payload); + +/** + * @struct + * + * The payload of ALTSVC frame. ALTSVC frame is a non-critical + * extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_ALTSVC`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The pointer to origin which this alternative service is + * associated with. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; + /** + * The pointer to Alt-Svc field value contained in ALTSVC frame. + * This is not necessarily NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the |field_value|. + */ + size_t field_value_len; +} nghttp2_ext_altsvc; + +/** + * @function + * + * Submits ALTSVC frame. + * + * ALTSVC frame is a non-critical extension to HTTP/2, and defined in + * `RFC 7383 `_. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |origin| points to the origin this alternative service is + * associated with. The |origin_len| is the length of the origin. If + * |stream_id| is 0, the origin must be specified. If |stream_id| is + * not zero, the origin must be empty (in other words, |origin_len| + * must be 0). + * + * The ALTSVC frame is only usable from server side. If this function + * is invoked with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The sum of |origin_len| and |field_value_len| is larger than + * 16382; or |origin_len| is 0 while |stream_id| is 0; or + * |origin_len| is not 0 while |stream_id| is not 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const uint8_t *origin, + size_t origin_len, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @struct + * + * The single entry of an origin. + */ +typedef struct { + /** + * The pointer to origin. No validation is made against this field + * by the library. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; +} nghttp2_origin_entry; + +/** + * @struct + * + * The payload of ORIGIN frame. ORIGIN frame is a non-critical + * extension to HTTP/2 and defined by `RFC 8336 + * `_. + * + * If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_ORIGIN`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The number of origins contained in |ov|. + */ + size_t nov; + /** + * The pointer to the array of origins contained in ORIGIN frame. + */ + nghttp2_origin_entry *ov; +} nghttp2_ext_origin; + +/** + * @function + * + * Submits ORIGIN frame. + * + * ORIGIN frame is a non-critical extension to HTTP/2 and defined by + * `RFC 8336 `_. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |ov| points to the array of origins. The |nov| specifies the + * number of origins included in |ov|. This function creates copies + * of all elements in |ov|. + * + * The ORIGIN frame is only usable by a server. If this function is + * invoked with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * There are too many origins, or an origin is too large to fit + * into a default frame payload. + */ +NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, + uint8_t flags, + const nghttp2_origin_entry *ov, + size_t nov); + +/** + * @struct + * + * The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a + * non-critical extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The stream ID of the stream whose priority is updated. + */ + int32_t stream_id; + /** + * The pointer to Priority field value. It is not necessarily + * NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the :member:`field_value`. + */ + size_t field_value_len; +} nghttp2_ext_priority_update; + +/** + * @function + * + * Submits PRIORITY_UPDATE frame. + * + * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and + * defined in :rfc:`9218#section-7.1`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the ID of stream which is prioritized. The + * |field_value| points to the Priority field value. The + * |field_value_len| is the length of the Priority field value. + * + * If this function is called by server, + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 0 is received by a remote endpoint (or it is omitted), + * this function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from server side session + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |field_value_len| is larger than 16380; or |stream_id| is + * 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @function + * + * Changes the priority of the existing stream denoted by |stream_id|. + * The new priority is |extpri|. This function is meant to be used by + * server for :rfc:`9218` extensible prioritization scheme. + * + * If |session| is initialized as client, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use + * `nghttp2_submit_priority_update()` instead. + * + * If :member:`extpri->urgency ` is out of + * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. + * + * If |ignore_client_signal| is nonzero, server starts to ignore + * client priority signals for this stream. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is not submitted via `nghttp2_submit_settings()`, + * this function does nothing and returns 0. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * |stream_id| is zero; or a stream denoted by |stream_id| is not + * found. + */ +NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority( + nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, + int ignore_client_signal); + +/** + * @function + * + * Stores the stream priority of the existing stream denoted by + * |stream_id| in the object pointed by |extpri|. This function is + * meant to be used by server for :rfc:`9218` extensible + * prioritization scheme. + * + * If |session| is initialized as client, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is not submitted via `nghttp2_submit_settings()`, + * this function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * |stream_id| is zero; or a stream denoted by |stream_id| is not + * found. + */ +NGHTTP2_EXTERN int nghttp2_session_get_extpri_stream_priority( + nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id); + +/** + * @function + * + * Parses Priority header field value pointed by |value| of length + * |len|, and stores the result in the object pointed by |extpri|. + * Priority header field is defined in :rfc:`9218`. + * + * This function does not initialize the object pointed by |extpri| + * before storing the result. It only assigns the values that the + * parser correctly extracted to fields. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Failed to parse the header field value. + */ +NGHTTP2_EXTERN int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri, + const uint8_t *value, + size_t len); + +/** + * @function + * + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_select_alpn` instead. + * + * A helper function for dealing with ALPN in server side. The |in| + * contains peer's protocol list in preferable order. The format of + * |in| is length-prefixed and not null-terminated. For example, + * ``h2`` and ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * To use this method you should do something like:: + * + * static int alpn_select_proto_cb(SSL* ssl, + * const unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol((unsigned char**)out, outlen, + * in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * A helper function for dealing with ALPN in server side. The |in| + * contains peer's protocol list in preferable order. The format of + * |in| is length-prefixed and not null-terminated. For example, + * ``h2`` and ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * To use this method you should do something like:: + * + * static int alpn_select_proto_cb(SSL* ssl, + * const unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_alpn(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_alpn(const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + * + * This function is considered obsolete, and application should + * consider to use `nghttp2_check_header_value_rfc9113()` instead. + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2, plus + * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value, + size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of + * the :method header field is valid according to + * https://datatracker.ietf.org/doc/html/rfc7231#section-4 and + * https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 + */ +NGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of + * the :path header field is valid according to + * https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3 + * + * |value| is valid if it merely consists of the allowed characters. + * In particular, it does not check whether |value| follows the syntax + * of path. The allowed characters are all characters valid by + * `nghttp2_check_header_value` minus SPC and HT. + */ +NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of the + * :authority or host header field is valid according to + * https://tools.ietf.org/html/rfc3986#section-3.2 + * + * Note that :authority and host field values are not authority. They + * do not include userinfo in RFC 3986, see + * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2, that + * is, it does not include '@'. This function treats '@' as a valid + * character. + * + * |value| is valid if it merely consists of the allowed characters. + * In particular, it does not check whether |value| follows the syntax + * of authority. + */ +NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len); + +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |max_deflate_dynamic_table_size| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_max_dynamic_table_size| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value received + * in SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``max_deflate_dynamic_table_size`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_max_dynamic_table_size| > + * ``max_deflate_dynamic_table_size``, resulting maximum table size + * becomes ``max_deflate_dynamic_table_size``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_max_dynamic_table_size); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_deflate_hd2()` instead. + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, + size_t nvlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize +nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nva, size_t nvlen); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_deflate_hd_vec2()` instead. + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this + * function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, + size_t veclen, + const nghttp2_nv *nva, + size_t nvlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this + * function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_deflate_hd_vec2( + nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns the number of entries that header table of |deflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of * static table. idx=62 returns first entry of dynamic table if it * exists. Specifying idx=0 is error, and this function returns NULL. * If |idx| is strictly greater than the number of entries the tables @@ -1531,7 +6440,7 @@ NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); * This function must not be called while header block is being * inflated. In other words, this function must be called after * initialization of |inflater|, but before calling - * `nghttp2_hd_inflate_hd2()`, or after + * `nghttp2_hd_inflate_hd3()`, or after * `nghttp2_hd_inflate_end_headers()`. Otherwise, * `NGHTTP2_ERR_INVALID_STATE` was returned. * @@ -1569,6 +6478,7 @@ typedef enum { NGHTTP2_HD_INFLATE_EMIT = 0x02 } nghttp2_hd_inflate_flag; +#ifndef NGHTTP2_NO_SSIZE_T /** * @function * @@ -1656,9 +6566,16 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, int *inflate_flags, uint8_t *in, size_t inlen, int in_final); +#endif /* NGHTTP2_NO_SSIZE_T */ + +#ifndef NGHTTP2_NO_SSIZE_T /** * @function * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_inflate_hd3()` instead. + * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of * header name/value pair, @@ -1745,6 +6662,95 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, const uint8_t *in, size_t inlen, int in_final); +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. If that happens, all given input + * data (|inlen| bytes) are processed successfully. Then the + * application must call `nghttp2_hd_inflate_end_headers()` to prepare + * for the next header block input. + * + * In other words, if |in_final| is nonzero, and this function returns + * |inlen|, you can assert that + * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in + * |*inflate_flags|. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * nghttp2_ssize rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd3(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %td", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_inflate_hd3( + nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final); + /** * @function * @@ -1798,6 +6804,32 @@ NGHTTP2_EXTERN size_t nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); +struct nghttp2_stream; + +/** + * @struct + * + * The structure to represent HTTP/2 stream. The details of this + * structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_stream nghttp2_stream; + +/** + * @function + * + * Returns pointer to :type:`nghttp2_stream` object denoted by + * |stream_id|. If stream was not found, returns NULL. + * + * Returns imaginary root stream (see + * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. + * + * Unless |stream_id| == 0, the returned pointer is valid until next + * call of `nghttp2_session_send()`, `nghttp2_session_mem_send2()`, + * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv2()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); + /** * @enum * @@ -1834,6 +6866,128 @@ typedef enum { NGHTTP2_STREAM_STATE_CLOSED } nghttp2_stream_proto_state; +/** + * @function + * + * Returns state of |stream|. The root stream retrieved by + * `nghttp2_session_get_root_stream()` will have stream state + * :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`. + */ +NGHTTP2_EXTERN nghttp2_stream_proto_state +nghttp2_stream_get_state(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Returns root of dependency tree, which is imaginary stream with + * stream ID 0. The returned pointer is valid until |session| is + * freed by `nghttp2_session_del()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_get_root_stream(nghttp2_session *session); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the parent stream of |stream| in dependency tree. Returns + * NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_parent(nghttp2_stream *stream); + +NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the next sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_next_sibling(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the previous sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the first child stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_first_child(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return + * :macro:`NGHTTP2_DEFAULT_WEIGHT`. + * + * Returns dependency weight to the parent stream of |stream|. + */ +NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0. + * + * Returns the sum of the weight for |stream|'s children. + */ +NGHTTP2_EXTERN int32_t +nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); + /** * @functypedef * @@ -1868,7 +7022,7 @@ typedef void (*nghttp2_debug_vprintf_callback)(const char *format, * this is important. */ NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( - nghttp2_debug_vprintf_callback debug_vprintf_callback); + nghttp2_debug_vprintf_callback debug_vprintf_callback); #ifdef __cplusplus } diff --git a/thirdparty/nghttp2/nghttp2_buf.c b/thirdparty/nghttp2/nghttp2_buf.c index e618400b012..3cdfe5be564 100644 --- a/thirdparty/nghttp2/nghttp2_buf.c +++ b/thirdparty/nghttp2/nghttp2_buf.c @@ -58,7 +58,7 @@ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { return 0; } - new_cap = nghttp2_max(new_cap, cap * 2); + new_cap = nghttp2_max_size(new_cap, cap * 2); ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); if (ptr == NULL) { @@ -340,7 +340,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { while (len) { buf = &bufs->cur->buf; - nwrite = nghttp2_min(nghttp2_buf_avail(buf), len); + nwrite = nghttp2_min_size(nghttp2_buf_avail(buf), len); if (nwrite == 0) { rv = bufs_alloc_chain(bufs); if (rv != 0) { @@ -427,7 +427,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { return 0; } -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { +nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { size_t len; nghttp2_buf_chain *chain; nghttp2_buf *buf; @@ -459,7 +459,7 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { *out = res; - return (ssize_t)len; + return (nghttp2_ssize)len; } size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { diff --git a/thirdparty/nghttp2/nghttp2_buf.h b/thirdparty/nghttp2/nghttp2_buf.h index eaddd868527..3603a5a2e15 100644 --- a/thirdparty/nghttp2/nghttp2_buf.h +++ b/thirdparty/nghttp2/nghttp2_buf.h @@ -94,7 +94,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); * |new_cap|. If extensions took place, buffer pointers in |buf| will * change. * - * This function returns 0 if it succeeds, or one of the followings + * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM @@ -344,7 +344,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); * NGHTTP2_ERR_NOMEM * Out of memory */ -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); +nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); /* * Copies all data stored in |bufs| to |out|. This function assumes diff --git a/thirdparty/nghttp2/nghttp2_hd.c b/thirdparty/nghttp2/nghttp2_hd.c index aa54243a665..e26ec93e548 100644 --- a/thirdparty/nghttp2/nghttp2_hd.c +++ b/thirdparty/nghttp2/nghttp2_hd.c @@ -32,9 +32,10 @@ #define MAKE_STATIC_ENT(N, V, T, H) \ { \ {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ - {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ - T, H \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ + {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ + T, \ + H, \ } /* Generated by mkstatictbl.py */ @@ -42,67 +43,67 @@ first enum value if same header names are repeated (e.g., :status). */ static const nghttp2_hd_static_entry static_table[] = { - MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), - MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), - MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), - MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), - MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), - MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), - MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), - MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), - MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), - MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), - MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), - MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), - MAKE_STATIC_ENT("accept", "", 18, 136609321u), - MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), - MAKE_STATIC_ENT("age", "", 20, 742476188u), - MAKE_STATIC_ENT("allow", "", 21, 2930878514u), - MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), - MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), - MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), - MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), - MAKE_STATIC_ENT("content-language", "", 26, 24973587u), - MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), - MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), - MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), - MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), - MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), - MAKE_STATIC_ENT("date", "", 32, 3564297305u), - MAKE_STATIC_ENT("etag", "", 33, 113792960u), - MAKE_STATIC_ENT("expect", "", 34, 2530896728u), - MAKE_STATIC_ENT("expires", "", 35, 1049544579u), - MAKE_STATIC_ENT("from", "", 36, 2513272949u), - MAKE_STATIC_ENT("host", "", 37, 2952701295u), - MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), - MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), - MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), - MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), - MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), - MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), - MAKE_STATIC_ENT("link", "", 44, 232457833u), - MAKE_STATIC_ENT("location", "", 45, 200649126u), - MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), - MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), - MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), - MAKE_STATIC_ENT("range", "", 49, 4208725202u), - MAKE_STATIC_ENT("referer", "", 50, 3969579366u), - MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), - MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), - MAKE_STATIC_ENT("server", "", 53, 1085029842u), - MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), - MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), - MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), - MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), - MAKE_STATIC_ENT("vary", "", 58, 1085005381u), - MAKE_STATIC_ENT("via", "", 59, 1762798611u), - MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), + MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), + MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), + MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), + MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), + MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), + MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), + MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), + MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), + MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), + MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), + MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), + MAKE_STATIC_ENT("accept", "", 18, 136609321u), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), + MAKE_STATIC_ENT("age", "", 20, 742476188u), + MAKE_STATIC_ENT("allow", "", 21, 2930878514u), + MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), + MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), + MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), + MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), + MAKE_STATIC_ENT("content-language", "", 26, 24973587u), + MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), + MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), + MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), + MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), + MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), + MAKE_STATIC_ENT("date", "", 32, 3564297305u), + MAKE_STATIC_ENT("etag", "", 33, 113792960u), + MAKE_STATIC_ENT("expect", "", 34, 2530896728u), + MAKE_STATIC_ENT("expires", "", 35, 1049544579u), + MAKE_STATIC_ENT("from", "", 36, 2513272949u), + MAKE_STATIC_ENT("host", "", 37, 2952701295u), + MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), + MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), + MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), + MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), + MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), + MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), + MAKE_STATIC_ENT("link", "", 44, 232457833u), + MAKE_STATIC_ENT("location", "", 45, 200649126u), + MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), + MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), + MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), + MAKE_STATIC_ENT("range", "", 49, 4208725202u), + MAKE_STATIC_ENT("referer", "", 50, 3969579366u), + MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), + MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), + MAKE_STATIC_ENT("server", "", 53, 1085029842u), + MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), + MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), + MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), + MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), + MAKE_STATIC_ENT("vary", "", 58, 1085005381u), + MAKE_STATIC_ENT("via", "", 59, 1762798611u), + MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), }; static int memeq(const void *s1, const void *s2, size_t n) { @@ -265,6 +266,11 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { return NGHTTP2_TOKEN_LOCATION; } break; + case 'y': + if (memeq("priorit", name, 7)) { + return NGHTTP2_TOKEN_PRIORITY; + } + break; } break; case 9: @@ -668,8 +674,8 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { context->bad = 0; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; rv = hd_ringbuf_init( - &context->hd_table, - context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); + &context->hd_table, + context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); if (rv != 0) { return rv; } @@ -686,7 +692,7 @@ static void hd_context_free(nghttp2_hd_context *context) { int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { return nghttp2_hd_deflate_init2( - deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); } int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, @@ -841,9 +847,10 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { * in the next call will be stored in |*shift_ptr|) and returns number * of bytes processed, or returns -1, indicating decoding error. */ -static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, const uint8_t *in, - const uint8_t *last, size_t prefix) { +static nghttp2_ssize decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, + const uint8_t *in, const uint8_t *last, + size_t prefix) { uint32_t k = (uint8_t)((1 << prefix) - 1); uint32_t n = initial; const uint8_t *start = in; @@ -862,7 +869,7 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, if (++in == last) { *res = n; - return (ssize_t)(in - start); + return (nghttp2_ssize)(in - start); } } @@ -897,12 +904,12 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, if (in == last) { *res = n; - return (ssize_t)(in - start); + return (nghttp2_ssize)(in - start); } *res = n; *fin = 1; - return (ssize_t)(in + 1 - start); + return (nghttp2_ssize)(in + 1 - start); } static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { @@ -1067,8 +1074,8 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, int rv; DEBUGF( - "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", - nv->namelen, nv->valuelen, indexing_mode); + "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode); rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); if (rv != 0) { @@ -1101,12 +1108,11 @@ static int add_hd_table_incremental(nghttp2_hd_context *context, while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && context->hd_table.len > 0) { - size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); + entry_room(ent->nv.name->len, ent->nv.value->len); DEBUGF("hpack: remove item from header table: %s: %s\n", (char *)ent->nv.name->base, (char *)ent->nv.value->base); @@ -1155,7 +1161,7 @@ static int add_hd_table_incremental(nghttp2_hd_context *context, } typedef struct { - ssize_t index; + nghttp2_ssize index; /* Nonzero if both name and value are matched. */ int name_value_match; } search_result; @@ -1204,8 +1210,8 @@ static search_result search_hd_table(nghttp2_hd_context *context, return res; } - res.index = - (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); + res.index = (nghttp2_ssize)(context->next_seq - 1 - ent->seq + + NGHTTP2_STATIC_TABLE_LENGTH); res.name_value_match = exact_match; return res; @@ -1222,7 +1228,7 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context, size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); + entry_room(ent->nv.name->len, ent->nv.value->len); hd_ringbuf_pop_back(&context->hd_table); if (map) { hd_map_remove(map, ent); @@ -1234,14 +1240,14 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context, } int nghttp2_hd_deflate_change_table_size( - nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { - size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size, - deflater->deflate_hd_table_bufsize_max); + nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { + size_t next_bufsize = nghttp2_min_size( + settings_max_dynamic_table_size, deflater->deflate_hd_table_bufsize_max); deflater->ctx.hd_table_bufsize_max = next_bufsize; deflater->min_hd_table_bufsize_max = - nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + nghttp2_min_size(deflater->min_hd_table_bufsize_max, next_bufsize); deflater->notify_table_size_change = 1; @@ -1250,7 +1256,7 @@ int nghttp2_hd_deflate_change_table_size( } int nghttp2_hd_inflate_change_table_size( - nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { + nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { switch (inflater->state) { case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: case NGHTTP2_HD_STATE_INFLATE_START: @@ -1259,6 +1265,8 @@ int nghttp2_hd_inflate_change_table_size( return NGHTTP2_ERR_INVALID_STATE; } + inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + /* It seems that encoder is not required to send dynamic table size update if the table size is not changed after applying SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this @@ -1271,13 +1279,12 @@ int nghttp2_hd_inflate_change_table_size( /* Remember minimum value, and validate that encoder sends the value less than or equal to this. */ inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; - } - inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; - inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; + hd_context_shrink_table_size(&inflater->ctx, NULL); + } - hd_context_shrink_table_size(&inflater->ctx, NULL); return 0; } @@ -1292,7 +1299,7 @@ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { assert(INDEX_RANGE_VALID(context, idx)); if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->nv; + ->nv; } else { const nghttp2_hd_static_entry *ent = &static_table[idx]; nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, @@ -1308,7 +1315,7 @@ static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return &hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->cnv; + ->cnv; } return &static_table[idx].cnv; @@ -1322,7 +1329,7 @@ static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || token == NGHTTP2_TOKEN_SET_COOKIE || entry_room(nv->namelen, nv->valuelen) > - deflater->ctx.hd_table_bufsize_max * 3 / 4) { + deflater->ctx.hd_table_bufsize_max * 3 / 4) { return NGHTTP2_HD_WITHOUT_INDEXING; } @@ -1333,7 +1340,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nv) { int rv; search_result res; - ssize_t idx; + nghttp2_ssize idx; int indexing_mode; int32_t token; nghttp2_mem *mem; @@ -1355,12 +1362,11 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, entropy secret data (e.g., id/password). Also cookie header field with less than 20 bytes value is also never indexed. This is the same criteria used in Firefox codebase. */ - indexing_mode = - token == NGHTTP2_TOKEN_AUTHORIZATION || - (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || - (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) - ? NGHTTP2_HD_NEVER_INDEXING - : hd_deflate_decide_indexing(deflater, nv, token); + indexing_mode = token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, &deflater->map, hash); @@ -1368,8 +1374,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, idx = res.index; if (res.name_value_match) { - - DEBUGF("deflatehd: name/value match index=%zd\n", idx); + DEBUGF("deflatehd: name/value match index=%td\n", idx); rv = emit_indexed_block(bufs, (size_t)idx); if (rv != 0) { @@ -1380,7 +1385,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, } if (res.index != -1) { - DEBUGF("deflatehd: name match index=%zd\n", res.index); + DEBUGF("deflatehd: name match index=%td\n", res.index); } if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { @@ -1446,7 +1451,6 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, deflater->min_hd_table_bufsize_max = UINT32_MAX; if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { - rv = emit_table_size(bufs, min_hd_table_bufsize_max); if (rv != 0) { @@ -1481,6 +1485,12 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nv, size_t nvlen) { + return (ssize_t)nghttp2_hd_deflate_hd2(deflater, buf, buflen, nv, nvlen); +} + +nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; @@ -1507,12 +1517,18 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, return rv; } - return (ssize_t)buflen; + return (nghttp2_ssize)buflen; } ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nv, size_t nvlen) { + return (ssize_t)nghttp2_hd_deflate_hd_vec2(deflater, vec, veclen, nv, nvlen); +} + +nghttp2_ssize nghttp2_hd_deflate_hd_vec2(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; @@ -1540,7 +1556,7 @@ ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, return rv; } - return (ssize_t)buflen; + return (nghttp2_ssize)buflen; } size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, @@ -1633,10 +1649,11 @@ static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, * NGHTTP2_ERR_HEADER_COMP * Integer decoding failed */ -static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, - const uint8_t *in, const uint8_t *last, - size_t prefix, size_t maxlen) { - ssize_t rv; +static nghttp2_ssize hd_inflate_read_len(nghttp2_hd_inflater *inflater, + int *rfin, const uint8_t *in, + const uint8_t *last, size_t prefix, + size_t maxlen) { + nghttp2_ssize rv; uint32_t out; *rfin = 0; @@ -1674,10 +1691,10 @@ static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, * NGHTTP2_ERR_HEADER_COMP * Huffman decoding failed */ -static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, - nghttp2_buf *buf, const uint8_t *in, - const uint8_t *last) { - ssize_t readlen; +static nghttp2_ssize hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + nghttp2_ssize readlen; int fin = 0; if ((size_t)(last - in) >= inflater->left) { last = in + inflater->left; @@ -1690,6 +1707,11 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, DEBUGF("inflatehd: huffman decoding failed\n"); return readlen; } + if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) { + DEBUGF("inflatehd: huffman decoding failed\n"); + return NGHTTP2_ERR_HEADER_COMP; + } + inflater->left -= (size_t)readlen; return readlen; } @@ -1706,14 +1728,15 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, * NGHTTP2_ERR_HEADER_COMP * Header decompression failed */ -static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, - const uint8_t *in, const uint8_t *last) { - size_t len = nghttp2_min((size_t)(last - in), inflater->left); +static nghttp2_ssize hd_inflate_read(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + size_t len = nghttp2_min_size((size_t)(last - in), inflater->left); buf->last = nghttp2_cpymem(buf->last, in, len); inflater->left -= len; - return (ssize_t)len; + return (nghttp2_ssize)len; } /* @@ -1828,7 +1851,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) { - ssize_t rv; + return (nghttp2_ssize)nghttp2_hd_inflate_hd3(inflater, nv_out, inflate_flags, + in, inlen, in_final); +} + +nghttp2_ssize nghttp2_hd_inflate_hd3(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final) { + nghttp2_ssize rv; nghttp2_hd_nv hd_nv; rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, @@ -1851,11 +1882,11 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, return rv; } -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, - int in_final) { - ssize_t rv = 0; +nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, + int *inflate_flags, const uint8_t *in, + size_t inlen, int in_final) { + nghttp2_ssize rv = 0; const uint8_t *first = in; const uint8_t *last = in + inlen; int rfin = 0; @@ -1923,9 +1954,9 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, case NGHTTP2_HD_STATE_READ_TABLE_SIZE: rfin = 0; rv = hd_inflate_read_len( - inflater, &rfin, in, last, 5, - nghttp2_min(inflater->min_hd_table_bufsize_max, - inflater->settings_hd_table_bufsize_max)); + inflater, &rfin, in, last, 5, + nghttp2_min_size(inflater->min_hd_table_bufsize_max, + inflater->settings_hd_table_bufsize_max)); if (rv < 0) { goto fail; } @@ -1977,7 +2008,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); } else { inflater->index = inflater->left; --inflater->index; @@ -2012,8 +2043,8 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; - rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, - mem); + rv = + nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); @@ -2035,7 +2066,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2057,7 +2088,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2097,8 +2128,8 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; - rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, - mem); + rv = + nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_READ_VALUE; @@ -2123,7 +2154,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2147,18 +2178,18 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); case NGHTTP2_HD_STATE_READ_VALUE: rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); if (rv < 0) { - DEBUGF("inflatehd: value read failure %zd: %s\n", rv, + DEBUGF("inflatehd: value read failure %td: %s\n", rv, nghttp2_strerror((int)rv)); goto fail; } in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2181,7 +2212,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); } } @@ -2201,7 +2232,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, } *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; } - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); almost_ok: if (in_final) { @@ -2211,10 +2242,10 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, goto fail; } - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); fail: - DEBUGF("inflatehd: error return %zd\n", rv); + DEBUGF("inflatehd: error return %td\n", rv); inflater->ctx.bad = 1; return rv; @@ -2269,7 +2300,6 @@ void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, nghttp2_nv *nv, int indexing_mode) { - return emit_indname_block(bufs, idx, nv, indexing_mode); } @@ -2282,9 +2312,10 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { return emit_table_size(bufs, table_size); } -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix) { +nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, + int *fin, uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, + size_t prefix) { return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); } diff --git a/thirdparty/nghttp2/nghttp2_hd.h b/thirdparty/nghttp2/nghttp2_hd.h index 6cd75e1310a..93bd48cff09 100644 --- a/thirdparty/nghttp2/nghttp2_hd.h +++ b/thirdparty/nghttp2/nghttp2_hd.h @@ -107,6 +107,7 @@ typedef enum { NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_UPGRADE, NGHTTP2_TOKEN__PROTOCOL, + NGHTTP2_TOKEN_PRIORITY, } nghttp2_token; struct nghttp2_hd_entry; @@ -351,9 +352,10 @@ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); * that return values and semantics are the same as * nghttp2_hd_inflate_hd(). */ -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, int in_final); +nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, + int *inflate_flags, const uint8_t *in, + size_t inlen, int in_final); /* For unittesting purpose */ int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, @@ -370,9 +372,10 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); /* For unittesting purpose */ -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix); +nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, + int *fin, uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, + size_t prefix); /* Huffman encoding/decoding functions */ @@ -421,9 +424,9 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); * NGHTTP2_ERR_HEADER_COMP * Decoding process has failed. */ -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int fin); +nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int fin); /* * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman.c b/thirdparty/nghttp2/nghttp2_hd_huffman.c index c0ea3bc3e6d..06db7f5df3f 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman.c @@ -93,7 +93,7 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, if (nbits) { rv = nghttp2_bufs_addb( - bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); + bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); if (rv != 0) { return rv; } @@ -106,16 +106,18 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { ctx->fstate = NGHTTP2_HUFF_ACCEPTED; } -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int final) { +nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int final) { const uint8_t *end = src + srclen; nghttp2_huff_decode node = {ctx->fstate, 0}; const nghttp2_huff_decode *t = &node; uint8_t c; /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf */ + - http://graphics.ics.uci.edu/pub/Prefix.pdf [!!! NO LONGER VALID !!!] + - https://ics.uci.edu/~dan/pubs/Prefix.pdf + - https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */ for (; src != end;) { c = *src++; t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; @@ -135,7 +137,7 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, return NGHTTP2_ERR_HEADER_COMP; } - return (ssize_t)srclen; + return (nghttp2_ssize)srclen; } int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c index 2e2e13f7bee..c8f4a6fa266 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c @@ -27,4954 +27,4954 @@ /* Generated by mkhufftbl.py */ const nghttp2_huff_sym huff_sym_table[] = { - {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, - {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, - {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, - {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, - {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, - {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, - {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, - {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, - {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, - {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, - {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, - {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, - {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, - {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, - {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, - {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, - {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, - {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, - {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, - {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, - {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, - {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, - {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, - {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, - {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, - {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, - {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, - {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, - {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, - {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, - {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, - {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, - {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, - {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, - {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, - {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, - {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, - {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, - {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, - {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, - {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, - {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, - {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, - {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, - {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, - {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, - {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, - {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, - {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, - {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, - {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, - {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, - {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, - {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, - {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, - {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, - {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, - {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, - {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, - {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, - {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, - {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, - {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, - {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, - {30, 0xfffffffcu}}; + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; const nghttp2_huff_decode huff_decode_table[][16] = { - /* 0 */ - { - {0x04, 0}, - {0x05, 0}, - {0x07, 0}, - {0x08, 0}, - {0x0b, 0}, - {0x0c, 0}, - {0x10, 0}, - {0x13, 0}, - {0x19, 0}, - {0x1c, 0}, - {0x20, 0}, - {0x23, 0}, - {0x2a, 0}, - {0x31, 0}, - {0x39, 0}, - {0x4040, 0}, - }, - /* 1 */ - { - {0xc000, 48}, - {0xc000, 49}, - {0xc000, 50}, - {0xc000, 97}, - {0xc000, 99}, - {0xc000, 101}, - {0xc000, 105}, - {0xc000, 111}, - {0xc000, 115}, - {0xc000, 116}, - {0x0d, 0}, - {0x0e, 0}, - {0x11, 0}, - {0x12, 0}, - {0x14, 0}, - {0x15, 0}, - }, - /* 2 */ - { - {0x8001, 48}, - {0xc016, 48}, - {0x8001, 49}, - {0xc016, 49}, - {0x8001, 50}, - {0xc016, 50}, - {0x8001, 97}, - {0xc016, 97}, - {0x8001, 99}, - {0xc016, 99}, - {0x8001, 101}, - {0xc016, 101}, - {0x8001, 105}, - {0xc016, 105}, - {0x8001, 111}, - {0xc016, 111}, - }, - /* 3 */ - { - {0x8002, 48}, - {0x8009, 48}, - {0x8017, 48}, - {0xc028, 48}, - {0x8002, 49}, - {0x8009, 49}, - {0x8017, 49}, - {0xc028, 49}, - {0x8002, 50}, - {0x8009, 50}, - {0x8017, 50}, - {0xc028, 50}, - {0x8002, 97}, - {0x8009, 97}, - {0x8017, 97}, - {0xc028, 97}, - }, - /* 4 */ - { - {0x8003, 48}, - {0x8006, 48}, - {0x800a, 48}, - {0x800f, 48}, - {0x8018, 48}, - {0x801f, 48}, - {0x8029, 48}, - {0xc038, 48}, - {0x8003, 49}, - {0x8006, 49}, - {0x800a, 49}, - {0x800f, 49}, - {0x8018, 49}, - {0x801f, 49}, - {0x8029, 49}, - {0xc038, 49}, - }, - /* 5 */ - { - {0x8003, 50}, - {0x8006, 50}, - {0x800a, 50}, - {0x800f, 50}, - {0x8018, 50}, - {0x801f, 50}, - {0x8029, 50}, - {0xc038, 50}, - {0x8003, 97}, - {0x8006, 97}, - {0x800a, 97}, - {0x800f, 97}, - {0x8018, 97}, - {0x801f, 97}, - {0x8029, 97}, - {0xc038, 97}, - }, - /* 6 */ - { - {0x8002, 99}, - {0x8009, 99}, - {0x8017, 99}, - {0xc028, 99}, - {0x8002, 101}, - {0x8009, 101}, - {0x8017, 101}, - {0xc028, 101}, - {0x8002, 105}, - {0x8009, 105}, - {0x8017, 105}, - {0xc028, 105}, - {0x8002, 111}, - {0x8009, 111}, - {0x8017, 111}, - {0xc028, 111}, - }, - /* 7 */ - { - {0x8003, 99}, - {0x8006, 99}, - {0x800a, 99}, - {0x800f, 99}, - {0x8018, 99}, - {0x801f, 99}, - {0x8029, 99}, - {0xc038, 99}, - {0x8003, 101}, - {0x8006, 101}, - {0x800a, 101}, - {0x800f, 101}, - {0x8018, 101}, - {0x801f, 101}, - {0x8029, 101}, - {0xc038, 101}, - }, - /* 8 */ - { - {0x8003, 105}, - {0x8006, 105}, - {0x800a, 105}, - {0x800f, 105}, - {0x8018, 105}, - {0x801f, 105}, - {0x8029, 105}, - {0xc038, 105}, - {0x8003, 111}, - {0x8006, 111}, - {0x800a, 111}, - {0x800f, 111}, - {0x8018, 111}, - {0x801f, 111}, - {0x8029, 111}, - {0xc038, 111}, - }, - /* 9 */ - { - {0x8001, 115}, - {0xc016, 115}, - {0x8001, 116}, - {0xc016, 116}, - {0xc000, 32}, - {0xc000, 37}, - {0xc000, 45}, - {0xc000, 46}, - {0xc000, 47}, - {0xc000, 51}, - {0xc000, 52}, - {0xc000, 53}, - {0xc000, 54}, - {0xc000, 55}, - {0xc000, 56}, - {0xc000, 57}, - }, - /* 10 */ - { - {0x8002, 115}, - {0x8009, 115}, - {0x8017, 115}, - {0xc028, 115}, - {0x8002, 116}, - {0x8009, 116}, - {0x8017, 116}, - {0xc028, 116}, - {0x8001, 32}, - {0xc016, 32}, - {0x8001, 37}, - {0xc016, 37}, - {0x8001, 45}, - {0xc016, 45}, - {0x8001, 46}, - {0xc016, 46}, - }, - /* 11 */ - { - {0x8003, 115}, - {0x8006, 115}, - {0x800a, 115}, - {0x800f, 115}, - {0x8018, 115}, - {0x801f, 115}, - {0x8029, 115}, - {0xc038, 115}, - {0x8003, 116}, - {0x8006, 116}, - {0x800a, 116}, - {0x800f, 116}, - {0x8018, 116}, - {0x801f, 116}, - {0x8029, 116}, - {0xc038, 116}, - }, - /* 12 */ - { - {0x8002, 32}, - {0x8009, 32}, - {0x8017, 32}, - {0xc028, 32}, - {0x8002, 37}, - {0x8009, 37}, - {0x8017, 37}, - {0xc028, 37}, - {0x8002, 45}, - {0x8009, 45}, - {0x8017, 45}, - {0xc028, 45}, - {0x8002, 46}, - {0x8009, 46}, - {0x8017, 46}, - {0xc028, 46}, - }, - /* 13 */ - { - {0x8003, 32}, - {0x8006, 32}, - {0x800a, 32}, - {0x800f, 32}, - {0x8018, 32}, - {0x801f, 32}, - {0x8029, 32}, - {0xc038, 32}, - {0x8003, 37}, - {0x8006, 37}, - {0x800a, 37}, - {0x800f, 37}, - {0x8018, 37}, - {0x801f, 37}, - {0x8029, 37}, - {0xc038, 37}, - }, - /* 14 */ - { - {0x8003, 45}, - {0x8006, 45}, - {0x800a, 45}, - {0x800f, 45}, - {0x8018, 45}, - {0x801f, 45}, - {0x8029, 45}, - {0xc038, 45}, - {0x8003, 46}, - {0x8006, 46}, - {0x800a, 46}, - {0x800f, 46}, - {0x8018, 46}, - {0x801f, 46}, - {0x8029, 46}, - {0xc038, 46}, - }, - /* 15 */ - { - {0x8001, 47}, - {0xc016, 47}, - {0x8001, 51}, - {0xc016, 51}, - {0x8001, 52}, - {0xc016, 52}, - {0x8001, 53}, - {0xc016, 53}, - {0x8001, 54}, - {0xc016, 54}, - {0x8001, 55}, - {0xc016, 55}, - {0x8001, 56}, - {0xc016, 56}, - {0x8001, 57}, - {0xc016, 57}, - }, - /* 16 */ - { - {0x8002, 47}, - {0x8009, 47}, - {0x8017, 47}, - {0xc028, 47}, - {0x8002, 51}, - {0x8009, 51}, - {0x8017, 51}, - {0xc028, 51}, - {0x8002, 52}, - {0x8009, 52}, - {0x8017, 52}, - {0xc028, 52}, - {0x8002, 53}, - {0x8009, 53}, - {0x8017, 53}, - {0xc028, 53}, - }, - /* 17 */ - { - {0x8003, 47}, - {0x8006, 47}, - {0x800a, 47}, - {0x800f, 47}, - {0x8018, 47}, - {0x801f, 47}, - {0x8029, 47}, - {0xc038, 47}, - {0x8003, 51}, - {0x8006, 51}, - {0x800a, 51}, - {0x800f, 51}, - {0x8018, 51}, - {0x801f, 51}, - {0x8029, 51}, - {0xc038, 51}, - }, - /* 18 */ - { - {0x8003, 52}, - {0x8006, 52}, - {0x800a, 52}, - {0x800f, 52}, - {0x8018, 52}, - {0x801f, 52}, - {0x8029, 52}, - {0xc038, 52}, - {0x8003, 53}, - {0x8006, 53}, - {0x800a, 53}, - {0x800f, 53}, - {0x8018, 53}, - {0x801f, 53}, - {0x8029, 53}, - {0xc038, 53}, - }, - /* 19 */ - { - {0x8002, 54}, - {0x8009, 54}, - {0x8017, 54}, - {0xc028, 54}, - {0x8002, 55}, - {0x8009, 55}, - {0x8017, 55}, - {0xc028, 55}, - {0x8002, 56}, - {0x8009, 56}, - {0x8017, 56}, - {0xc028, 56}, - {0x8002, 57}, - {0x8009, 57}, - {0x8017, 57}, - {0xc028, 57}, - }, - /* 20 */ - { - {0x8003, 54}, - {0x8006, 54}, - {0x800a, 54}, - {0x800f, 54}, - {0x8018, 54}, - {0x801f, 54}, - {0x8029, 54}, - {0xc038, 54}, - {0x8003, 55}, - {0x8006, 55}, - {0x800a, 55}, - {0x800f, 55}, - {0x8018, 55}, - {0x801f, 55}, - {0x8029, 55}, - {0xc038, 55}, - }, - /* 21 */ - { - {0x8003, 56}, - {0x8006, 56}, - {0x800a, 56}, - {0x800f, 56}, - {0x8018, 56}, - {0x801f, 56}, - {0x8029, 56}, - {0xc038, 56}, - {0x8003, 57}, - {0x8006, 57}, - {0x800a, 57}, - {0x800f, 57}, - {0x8018, 57}, - {0x801f, 57}, - {0x8029, 57}, - {0xc038, 57}, - }, - /* 22 */ - { - {0x1a, 0}, - {0x1b, 0}, - {0x1d, 0}, - {0x1e, 0}, - {0x21, 0}, - {0x22, 0}, - {0x24, 0}, - {0x25, 0}, - {0x2b, 0}, - {0x2e, 0}, - {0x32, 0}, - {0x35, 0}, - {0x3a, 0}, - {0x3d, 0}, - {0x41, 0}, - {0x4044, 0}, - }, - /* 23 */ - { - {0xc000, 61}, - {0xc000, 65}, - {0xc000, 95}, - {0xc000, 98}, - {0xc000, 100}, - {0xc000, 102}, - {0xc000, 103}, - {0xc000, 104}, - {0xc000, 108}, - {0xc000, 109}, - {0xc000, 110}, - {0xc000, 112}, - {0xc000, 114}, - {0xc000, 117}, - {0x26, 0}, - {0x27, 0}, - }, - /* 24 */ - { - {0x8001, 61}, - {0xc016, 61}, - {0x8001, 65}, - {0xc016, 65}, - {0x8001, 95}, - {0xc016, 95}, - {0x8001, 98}, - {0xc016, 98}, - {0x8001, 100}, - {0xc016, 100}, - {0x8001, 102}, - {0xc016, 102}, - {0x8001, 103}, - {0xc016, 103}, - {0x8001, 104}, - {0xc016, 104}, - }, - /* 25 */ - { - {0x8002, 61}, - {0x8009, 61}, - {0x8017, 61}, - {0xc028, 61}, - {0x8002, 65}, - {0x8009, 65}, - {0x8017, 65}, - {0xc028, 65}, - {0x8002, 95}, - {0x8009, 95}, - {0x8017, 95}, - {0xc028, 95}, - {0x8002, 98}, - {0x8009, 98}, - {0x8017, 98}, - {0xc028, 98}, - }, - /* 26 */ - { - {0x8003, 61}, - {0x8006, 61}, - {0x800a, 61}, - {0x800f, 61}, - {0x8018, 61}, - {0x801f, 61}, - {0x8029, 61}, - {0xc038, 61}, - {0x8003, 65}, - {0x8006, 65}, - {0x800a, 65}, - {0x800f, 65}, - {0x8018, 65}, - {0x801f, 65}, - {0x8029, 65}, - {0xc038, 65}, - }, - /* 27 */ - { - {0x8003, 95}, - {0x8006, 95}, - {0x800a, 95}, - {0x800f, 95}, - {0x8018, 95}, - {0x801f, 95}, - {0x8029, 95}, - {0xc038, 95}, - {0x8003, 98}, - {0x8006, 98}, - {0x800a, 98}, - {0x800f, 98}, - {0x8018, 98}, - {0x801f, 98}, - {0x8029, 98}, - {0xc038, 98}, - }, - /* 28 */ - { - {0x8002, 100}, - {0x8009, 100}, - {0x8017, 100}, - {0xc028, 100}, - {0x8002, 102}, - {0x8009, 102}, - {0x8017, 102}, - {0xc028, 102}, - {0x8002, 103}, - {0x8009, 103}, - {0x8017, 103}, - {0xc028, 103}, - {0x8002, 104}, - {0x8009, 104}, - {0x8017, 104}, - {0xc028, 104}, - }, - /* 29 */ - { - {0x8003, 100}, - {0x8006, 100}, - {0x800a, 100}, - {0x800f, 100}, - {0x8018, 100}, - {0x801f, 100}, - {0x8029, 100}, - {0xc038, 100}, - {0x8003, 102}, - {0x8006, 102}, - {0x800a, 102}, - {0x800f, 102}, - {0x8018, 102}, - {0x801f, 102}, - {0x8029, 102}, - {0xc038, 102}, - }, - /* 30 */ - { - {0x8003, 103}, - {0x8006, 103}, - {0x800a, 103}, - {0x800f, 103}, - {0x8018, 103}, - {0x801f, 103}, - {0x8029, 103}, - {0xc038, 103}, - {0x8003, 104}, - {0x8006, 104}, - {0x800a, 104}, - {0x800f, 104}, - {0x8018, 104}, - {0x801f, 104}, - {0x8029, 104}, - {0xc038, 104}, - }, - /* 31 */ - { - {0x8001, 108}, - {0xc016, 108}, - {0x8001, 109}, - {0xc016, 109}, - {0x8001, 110}, - {0xc016, 110}, - {0x8001, 112}, - {0xc016, 112}, - {0x8001, 114}, - {0xc016, 114}, - {0x8001, 117}, - {0xc016, 117}, - {0xc000, 58}, - {0xc000, 66}, - {0xc000, 67}, - {0xc000, 68}, - }, - /* 32 */ - { - {0x8002, 108}, - {0x8009, 108}, - {0x8017, 108}, - {0xc028, 108}, - {0x8002, 109}, - {0x8009, 109}, - {0x8017, 109}, - {0xc028, 109}, - {0x8002, 110}, - {0x8009, 110}, - {0x8017, 110}, - {0xc028, 110}, - {0x8002, 112}, - {0x8009, 112}, - {0x8017, 112}, - {0xc028, 112}, - }, - /* 33 */ - { - {0x8003, 108}, - {0x8006, 108}, - {0x800a, 108}, - {0x800f, 108}, - {0x8018, 108}, - {0x801f, 108}, - {0x8029, 108}, - {0xc038, 108}, - {0x8003, 109}, - {0x8006, 109}, - {0x800a, 109}, - {0x800f, 109}, - {0x8018, 109}, - {0x801f, 109}, - {0x8029, 109}, - {0xc038, 109}, - }, - /* 34 */ - { - {0x8003, 110}, - {0x8006, 110}, - {0x800a, 110}, - {0x800f, 110}, - {0x8018, 110}, - {0x801f, 110}, - {0x8029, 110}, - {0xc038, 110}, - {0x8003, 112}, - {0x8006, 112}, - {0x800a, 112}, - {0x800f, 112}, - {0x8018, 112}, - {0x801f, 112}, - {0x8029, 112}, - {0xc038, 112}, - }, - /* 35 */ - { - {0x8002, 114}, - {0x8009, 114}, - {0x8017, 114}, - {0xc028, 114}, - {0x8002, 117}, - {0x8009, 117}, - {0x8017, 117}, - {0xc028, 117}, - {0x8001, 58}, - {0xc016, 58}, - {0x8001, 66}, - {0xc016, 66}, - {0x8001, 67}, - {0xc016, 67}, - {0x8001, 68}, - {0xc016, 68}, - }, - /* 36 */ - { - {0x8003, 114}, - {0x8006, 114}, - {0x800a, 114}, - {0x800f, 114}, - {0x8018, 114}, - {0x801f, 114}, - {0x8029, 114}, - {0xc038, 114}, - {0x8003, 117}, - {0x8006, 117}, - {0x800a, 117}, - {0x800f, 117}, - {0x8018, 117}, - {0x801f, 117}, - {0x8029, 117}, - {0xc038, 117}, - }, - /* 37 */ - { - {0x8002, 58}, - {0x8009, 58}, - {0x8017, 58}, - {0xc028, 58}, - {0x8002, 66}, - {0x8009, 66}, - {0x8017, 66}, - {0xc028, 66}, - {0x8002, 67}, - {0x8009, 67}, - {0x8017, 67}, - {0xc028, 67}, - {0x8002, 68}, - {0x8009, 68}, - {0x8017, 68}, - {0xc028, 68}, - }, - /* 38 */ - { - {0x8003, 58}, - {0x8006, 58}, - {0x800a, 58}, - {0x800f, 58}, - {0x8018, 58}, - {0x801f, 58}, - {0x8029, 58}, - {0xc038, 58}, - {0x8003, 66}, - {0x8006, 66}, - {0x800a, 66}, - {0x800f, 66}, - {0x8018, 66}, - {0x801f, 66}, - {0x8029, 66}, - {0xc038, 66}, - }, - /* 39 */ - { - {0x8003, 67}, - {0x8006, 67}, - {0x800a, 67}, - {0x800f, 67}, - {0x8018, 67}, - {0x801f, 67}, - {0x8029, 67}, - {0xc038, 67}, - {0x8003, 68}, - {0x8006, 68}, - {0x800a, 68}, - {0x800f, 68}, - {0x8018, 68}, - {0x801f, 68}, - {0x8029, 68}, - {0xc038, 68}, - }, - /* 40 */ - { - {0x2c, 0}, - {0x2d, 0}, - {0x2f, 0}, - {0x30, 0}, - {0x33, 0}, - {0x34, 0}, - {0x36, 0}, - {0x37, 0}, - {0x3b, 0}, - {0x3c, 0}, - {0x3e, 0}, - {0x3f, 0}, - {0x42, 0}, - {0x43, 0}, - {0x45, 0}, - {0x4048, 0}, - }, - /* 41 */ - { - {0xc000, 69}, - {0xc000, 70}, - {0xc000, 71}, - {0xc000, 72}, - {0xc000, 73}, - {0xc000, 74}, - {0xc000, 75}, - {0xc000, 76}, - {0xc000, 77}, - {0xc000, 78}, - {0xc000, 79}, - {0xc000, 80}, - {0xc000, 81}, - {0xc000, 82}, - {0xc000, 83}, - {0xc000, 84}, - }, - /* 42 */ - { - {0x8001, 69}, - {0xc016, 69}, - {0x8001, 70}, - {0xc016, 70}, - {0x8001, 71}, - {0xc016, 71}, - {0x8001, 72}, - {0xc016, 72}, - {0x8001, 73}, - {0xc016, 73}, - {0x8001, 74}, - {0xc016, 74}, - {0x8001, 75}, - {0xc016, 75}, - {0x8001, 76}, - {0xc016, 76}, - }, - /* 43 */ - { - {0x8002, 69}, - {0x8009, 69}, - {0x8017, 69}, - {0xc028, 69}, - {0x8002, 70}, - {0x8009, 70}, - {0x8017, 70}, - {0xc028, 70}, - {0x8002, 71}, - {0x8009, 71}, - {0x8017, 71}, - {0xc028, 71}, - {0x8002, 72}, - {0x8009, 72}, - {0x8017, 72}, - {0xc028, 72}, - }, - /* 44 */ - { - {0x8003, 69}, - {0x8006, 69}, - {0x800a, 69}, - {0x800f, 69}, - {0x8018, 69}, - {0x801f, 69}, - {0x8029, 69}, - {0xc038, 69}, - {0x8003, 70}, - {0x8006, 70}, - {0x800a, 70}, - {0x800f, 70}, - {0x8018, 70}, - {0x801f, 70}, - {0x8029, 70}, - {0xc038, 70}, - }, - /* 45 */ - { - {0x8003, 71}, - {0x8006, 71}, - {0x800a, 71}, - {0x800f, 71}, - {0x8018, 71}, - {0x801f, 71}, - {0x8029, 71}, - {0xc038, 71}, - {0x8003, 72}, - {0x8006, 72}, - {0x800a, 72}, - {0x800f, 72}, - {0x8018, 72}, - {0x801f, 72}, - {0x8029, 72}, - {0xc038, 72}, - }, - /* 46 */ - { - {0x8002, 73}, - {0x8009, 73}, - {0x8017, 73}, - {0xc028, 73}, - {0x8002, 74}, - {0x8009, 74}, - {0x8017, 74}, - {0xc028, 74}, - {0x8002, 75}, - {0x8009, 75}, - {0x8017, 75}, - {0xc028, 75}, - {0x8002, 76}, - {0x8009, 76}, - {0x8017, 76}, - {0xc028, 76}, - }, - /* 47 */ - { - {0x8003, 73}, - {0x8006, 73}, - {0x800a, 73}, - {0x800f, 73}, - {0x8018, 73}, - {0x801f, 73}, - {0x8029, 73}, - {0xc038, 73}, - {0x8003, 74}, - {0x8006, 74}, - {0x800a, 74}, - {0x800f, 74}, - {0x8018, 74}, - {0x801f, 74}, - {0x8029, 74}, - {0xc038, 74}, - }, - /* 48 */ - { - {0x8003, 75}, - {0x8006, 75}, - {0x800a, 75}, - {0x800f, 75}, - {0x8018, 75}, - {0x801f, 75}, - {0x8029, 75}, - {0xc038, 75}, - {0x8003, 76}, - {0x8006, 76}, - {0x800a, 76}, - {0x800f, 76}, - {0x8018, 76}, - {0x801f, 76}, - {0x8029, 76}, - {0xc038, 76}, - }, - /* 49 */ - { - {0x8001, 77}, - {0xc016, 77}, - {0x8001, 78}, - {0xc016, 78}, - {0x8001, 79}, - {0xc016, 79}, - {0x8001, 80}, - {0xc016, 80}, - {0x8001, 81}, - {0xc016, 81}, - {0x8001, 82}, - {0xc016, 82}, - {0x8001, 83}, - {0xc016, 83}, - {0x8001, 84}, - {0xc016, 84}, - }, - /* 50 */ - { - {0x8002, 77}, - {0x8009, 77}, - {0x8017, 77}, - {0xc028, 77}, - {0x8002, 78}, - {0x8009, 78}, - {0x8017, 78}, - {0xc028, 78}, - {0x8002, 79}, - {0x8009, 79}, - {0x8017, 79}, - {0xc028, 79}, - {0x8002, 80}, - {0x8009, 80}, - {0x8017, 80}, - {0xc028, 80}, - }, - /* 51 */ - { - {0x8003, 77}, - {0x8006, 77}, - {0x800a, 77}, - {0x800f, 77}, - {0x8018, 77}, - {0x801f, 77}, - {0x8029, 77}, - {0xc038, 77}, - {0x8003, 78}, - {0x8006, 78}, - {0x800a, 78}, - {0x800f, 78}, - {0x8018, 78}, - {0x801f, 78}, - {0x8029, 78}, - {0xc038, 78}, - }, - /* 52 */ - { - {0x8003, 79}, - {0x8006, 79}, - {0x800a, 79}, - {0x800f, 79}, - {0x8018, 79}, - {0x801f, 79}, - {0x8029, 79}, - {0xc038, 79}, - {0x8003, 80}, - {0x8006, 80}, - {0x800a, 80}, - {0x800f, 80}, - {0x8018, 80}, - {0x801f, 80}, - {0x8029, 80}, - {0xc038, 80}, - }, - /* 53 */ - { - {0x8002, 81}, - {0x8009, 81}, - {0x8017, 81}, - {0xc028, 81}, - {0x8002, 82}, - {0x8009, 82}, - {0x8017, 82}, - {0xc028, 82}, - {0x8002, 83}, - {0x8009, 83}, - {0x8017, 83}, - {0xc028, 83}, - {0x8002, 84}, - {0x8009, 84}, - {0x8017, 84}, - {0xc028, 84}, - }, - /* 54 */ - { - {0x8003, 81}, - {0x8006, 81}, - {0x800a, 81}, - {0x800f, 81}, - {0x8018, 81}, - {0x801f, 81}, - {0x8029, 81}, - {0xc038, 81}, - {0x8003, 82}, - {0x8006, 82}, - {0x800a, 82}, - {0x800f, 82}, - {0x8018, 82}, - {0x801f, 82}, - {0x8029, 82}, - {0xc038, 82}, - }, - /* 55 */ - { - {0x8003, 83}, - {0x8006, 83}, - {0x800a, 83}, - {0x800f, 83}, - {0x8018, 83}, - {0x801f, 83}, - {0x8029, 83}, - {0xc038, 83}, - {0x8003, 84}, - {0x8006, 84}, - {0x800a, 84}, - {0x800f, 84}, - {0x8018, 84}, - {0x801f, 84}, - {0x8029, 84}, - {0xc038, 84}, - }, - /* 56 */ - { - {0xc000, 85}, - {0xc000, 86}, - {0xc000, 87}, - {0xc000, 89}, - {0xc000, 106}, - {0xc000, 107}, - {0xc000, 113}, - {0xc000, 118}, - {0xc000, 119}, - {0xc000, 120}, - {0xc000, 121}, - {0xc000, 122}, - {0x46, 0}, - {0x47, 0}, - {0x49, 0}, - {0x404a, 0}, - }, - /* 57 */ - { - {0x8001, 85}, - {0xc016, 85}, - {0x8001, 86}, - {0xc016, 86}, - {0x8001, 87}, - {0xc016, 87}, - {0x8001, 89}, - {0xc016, 89}, - {0x8001, 106}, - {0xc016, 106}, - {0x8001, 107}, - {0xc016, 107}, - {0x8001, 113}, - {0xc016, 113}, - {0x8001, 118}, - {0xc016, 118}, - }, - /* 58 */ - { - {0x8002, 85}, - {0x8009, 85}, - {0x8017, 85}, - {0xc028, 85}, - {0x8002, 86}, - {0x8009, 86}, - {0x8017, 86}, - {0xc028, 86}, - {0x8002, 87}, - {0x8009, 87}, - {0x8017, 87}, - {0xc028, 87}, - {0x8002, 89}, - {0x8009, 89}, - {0x8017, 89}, - {0xc028, 89}, - }, - /* 59 */ - { - {0x8003, 85}, - {0x8006, 85}, - {0x800a, 85}, - {0x800f, 85}, - {0x8018, 85}, - {0x801f, 85}, - {0x8029, 85}, - {0xc038, 85}, - {0x8003, 86}, - {0x8006, 86}, - {0x800a, 86}, - {0x800f, 86}, - {0x8018, 86}, - {0x801f, 86}, - {0x8029, 86}, - {0xc038, 86}, - }, - /* 60 */ - { - {0x8003, 87}, - {0x8006, 87}, - {0x800a, 87}, - {0x800f, 87}, - {0x8018, 87}, - {0x801f, 87}, - {0x8029, 87}, - {0xc038, 87}, - {0x8003, 89}, - {0x8006, 89}, - {0x800a, 89}, - {0x800f, 89}, - {0x8018, 89}, - {0x801f, 89}, - {0x8029, 89}, - {0xc038, 89}, - }, - /* 61 */ - { - {0x8002, 106}, - {0x8009, 106}, - {0x8017, 106}, - {0xc028, 106}, - {0x8002, 107}, - {0x8009, 107}, - {0x8017, 107}, - {0xc028, 107}, - {0x8002, 113}, - {0x8009, 113}, - {0x8017, 113}, - {0xc028, 113}, - {0x8002, 118}, - {0x8009, 118}, - {0x8017, 118}, - {0xc028, 118}, - }, - /* 62 */ - { - {0x8003, 106}, - {0x8006, 106}, - {0x800a, 106}, - {0x800f, 106}, - {0x8018, 106}, - {0x801f, 106}, - {0x8029, 106}, - {0xc038, 106}, - {0x8003, 107}, - {0x8006, 107}, - {0x800a, 107}, - {0x800f, 107}, - {0x8018, 107}, - {0x801f, 107}, - {0x8029, 107}, - {0xc038, 107}, - }, - /* 63 */ - { - {0x8003, 113}, - {0x8006, 113}, - {0x800a, 113}, - {0x800f, 113}, - {0x8018, 113}, - {0x801f, 113}, - {0x8029, 113}, - {0xc038, 113}, - {0x8003, 118}, - {0x8006, 118}, - {0x800a, 118}, - {0x800f, 118}, - {0x8018, 118}, - {0x801f, 118}, - {0x8029, 118}, - {0xc038, 118}, - }, - /* 64 */ - { - {0x8001, 119}, - {0xc016, 119}, - {0x8001, 120}, - {0xc016, 120}, - {0x8001, 121}, - {0xc016, 121}, - {0x8001, 122}, - {0xc016, 122}, - {0xc000, 38}, - {0xc000, 42}, - {0xc000, 44}, - {0xc000, 59}, - {0xc000, 88}, - {0xc000, 90}, - {0x4b, 0}, - {0x4e, 0}, - }, - /* 65 */ - { - {0x8002, 119}, - {0x8009, 119}, - {0x8017, 119}, - {0xc028, 119}, - {0x8002, 120}, - {0x8009, 120}, - {0x8017, 120}, - {0xc028, 120}, - {0x8002, 121}, - {0x8009, 121}, - {0x8017, 121}, - {0xc028, 121}, - {0x8002, 122}, - {0x8009, 122}, - {0x8017, 122}, - {0xc028, 122}, - }, - /* 66 */ - { - {0x8003, 119}, - {0x8006, 119}, - {0x800a, 119}, - {0x800f, 119}, - {0x8018, 119}, - {0x801f, 119}, - {0x8029, 119}, - {0xc038, 119}, - {0x8003, 120}, - {0x8006, 120}, - {0x800a, 120}, - {0x800f, 120}, - {0x8018, 120}, - {0x801f, 120}, - {0x8029, 120}, - {0xc038, 120}, - }, - /* 67 */ - { - {0x8003, 121}, - {0x8006, 121}, - {0x800a, 121}, - {0x800f, 121}, - {0x8018, 121}, - {0x801f, 121}, - {0x8029, 121}, - {0xc038, 121}, - {0x8003, 122}, - {0x8006, 122}, - {0x800a, 122}, - {0x800f, 122}, - {0x8018, 122}, - {0x801f, 122}, - {0x8029, 122}, - {0xc038, 122}, - }, - /* 68 */ - { - {0x8001, 38}, - {0xc016, 38}, - {0x8001, 42}, - {0xc016, 42}, - {0x8001, 44}, - {0xc016, 44}, - {0x8001, 59}, - {0xc016, 59}, - {0x8001, 88}, - {0xc016, 88}, - {0x8001, 90}, - {0xc016, 90}, - {0x4c, 0}, - {0x4d, 0}, - {0x4f, 0}, - {0x51, 0}, - }, - /* 69 */ - { - {0x8002, 38}, - {0x8009, 38}, - {0x8017, 38}, - {0xc028, 38}, - {0x8002, 42}, - {0x8009, 42}, - {0x8017, 42}, - {0xc028, 42}, - {0x8002, 44}, - {0x8009, 44}, - {0x8017, 44}, - {0xc028, 44}, - {0x8002, 59}, - {0x8009, 59}, - {0x8017, 59}, - {0xc028, 59}, - }, - /* 70 */ - { - {0x8003, 38}, - {0x8006, 38}, - {0x800a, 38}, - {0x800f, 38}, - {0x8018, 38}, - {0x801f, 38}, - {0x8029, 38}, - {0xc038, 38}, - {0x8003, 42}, - {0x8006, 42}, - {0x800a, 42}, - {0x800f, 42}, - {0x8018, 42}, - {0x801f, 42}, - {0x8029, 42}, - {0xc038, 42}, - }, - /* 71 */ - { - {0x8003, 44}, - {0x8006, 44}, - {0x800a, 44}, - {0x800f, 44}, - {0x8018, 44}, - {0x801f, 44}, - {0x8029, 44}, - {0xc038, 44}, - {0x8003, 59}, - {0x8006, 59}, - {0x800a, 59}, - {0x800f, 59}, - {0x8018, 59}, - {0x801f, 59}, - {0x8029, 59}, - {0xc038, 59}, - }, - /* 72 */ - { - {0x8002, 88}, - {0x8009, 88}, - {0x8017, 88}, - {0xc028, 88}, - {0x8002, 90}, - {0x8009, 90}, - {0x8017, 90}, - {0xc028, 90}, - {0xc000, 33}, - {0xc000, 34}, - {0xc000, 40}, - {0xc000, 41}, - {0xc000, 63}, - {0x50, 0}, - {0x52, 0}, - {0x54, 0}, - }, - /* 73 */ - { - {0x8003, 88}, - {0x8006, 88}, - {0x800a, 88}, - {0x800f, 88}, - {0x8018, 88}, - {0x801f, 88}, - {0x8029, 88}, - {0xc038, 88}, - {0x8003, 90}, - {0x8006, 90}, - {0x800a, 90}, - {0x800f, 90}, - {0x8018, 90}, - {0x801f, 90}, - {0x8029, 90}, - {0xc038, 90}, - }, - /* 74 */ - { - {0x8001, 33}, - {0xc016, 33}, - {0x8001, 34}, - {0xc016, 34}, - {0x8001, 40}, - {0xc016, 40}, - {0x8001, 41}, - {0xc016, 41}, - {0x8001, 63}, - {0xc016, 63}, - {0xc000, 39}, - {0xc000, 43}, - {0xc000, 124}, - {0x53, 0}, - {0x55, 0}, - {0x58, 0}, - }, - /* 75 */ - { - {0x8002, 33}, - {0x8009, 33}, - {0x8017, 33}, - {0xc028, 33}, - {0x8002, 34}, - {0x8009, 34}, - {0x8017, 34}, - {0xc028, 34}, - {0x8002, 40}, - {0x8009, 40}, - {0x8017, 40}, - {0xc028, 40}, - {0x8002, 41}, - {0x8009, 41}, - {0x8017, 41}, - {0xc028, 41}, - }, - /* 76 */ - { - {0x8003, 33}, - {0x8006, 33}, - {0x800a, 33}, - {0x800f, 33}, - {0x8018, 33}, - {0x801f, 33}, - {0x8029, 33}, - {0xc038, 33}, - {0x8003, 34}, - {0x8006, 34}, - {0x800a, 34}, - {0x800f, 34}, - {0x8018, 34}, - {0x801f, 34}, - {0x8029, 34}, - {0xc038, 34}, - }, - /* 77 */ - { - {0x8003, 40}, - {0x8006, 40}, - {0x800a, 40}, - {0x800f, 40}, - {0x8018, 40}, - {0x801f, 40}, - {0x8029, 40}, - {0xc038, 40}, - {0x8003, 41}, - {0x8006, 41}, - {0x800a, 41}, - {0x800f, 41}, - {0x8018, 41}, - {0x801f, 41}, - {0x8029, 41}, - {0xc038, 41}, - }, - /* 78 */ - { - {0x8002, 63}, - {0x8009, 63}, - {0x8017, 63}, - {0xc028, 63}, - {0x8001, 39}, - {0xc016, 39}, - {0x8001, 43}, - {0xc016, 43}, - {0x8001, 124}, - {0xc016, 124}, - {0xc000, 35}, - {0xc000, 62}, - {0x56, 0}, - {0x57, 0}, - {0x59, 0}, - {0x5a, 0}, - }, - /* 79 */ - { - {0x8003, 63}, - {0x8006, 63}, - {0x800a, 63}, - {0x800f, 63}, - {0x8018, 63}, - {0x801f, 63}, - {0x8029, 63}, - {0xc038, 63}, - {0x8002, 39}, - {0x8009, 39}, - {0x8017, 39}, - {0xc028, 39}, - {0x8002, 43}, - {0x8009, 43}, - {0x8017, 43}, - {0xc028, 43}, - }, - /* 80 */ - { - {0x8003, 39}, - {0x8006, 39}, - {0x800a, 39}, - {0x800f, 39}, - {0x8018, 39}, - {0x801f, 39}, - {0x8029, 39}, - {0xc038, 39}, - {0x8003, 43}, - {0x8006, 43}, - {0x800a, 43}, - {0x800f, 43}, - {0x8018, 43}, - {0x801f, 43}, - {0x8029, 43}, - {0xc038, 43}, - }, - /* 81 */ - { - {0x8002, 124}, - {0x8009, 124}, - {0x8017, 124}, - {0xc028, 124}, - {0x8001, 35}, - {0xc016, 35}, - {0x8001, 62}, - {0xc016, 62}, - {0xc000, 0}, - {0xc000, 36}, - {0xc000, 64}, - {0xc000, 91}, - {0xc000, 93}, - {0xc000, 126}, - {0x5b, 0}, - {0x5c, 0}, - }, - /* 82 */ - { - {0x8003, 124}, - {0x8006, 124}, - {0x800a, 124}, - {0x800f, 124}, - {0x8018, 124}, - {0x801f, 124}, - {0x8029, 124}, - {0xc038, 124}, - {0x8002, 35}, - {0x8009, 35}, - {0x8017, 35}, - {0xc028, 35}, - {0x8002, 62}, - {0x8009, 62}, - {0x8017, 62}, - {0xc028, 62}, - }, - /* 83 */ - { - {0x8003, 35}, - {0x8006, 35}, - {0x800a, 35}, - {0x800f, 35}, - {0x8018, 35}, - {0x801f, 35}, - {0x8029, 35}, - {0xc038, 35}, - {0x8003, 62}, - {0x8006, 62}, - {0x800a, 62}, - {0x800f, 62}, - {0x8018, 62}, - {0x801f, 62}, - {0x8029, 62}, - {0xc038, 62}, - }, - /* 84 */ - { - {0x8001, 0}, - {0xc016, 0}, - {0x8001, 36}, - {0xc016, 36}, - {0x8001, 64}, - {0xc016, 64}, - {0x8001, 91}, - {0xc016, 91}, - {0x8001, 93}, - {0xc016, 93}, - {0x8001, 126}, - {0xc016, 126}, - {0xc000, 94}, - {0xc000, 125}, - {0x5d, 0}, - {0x5e, 0}, - }, - /* 85 */ - { - {0x8002, 0}, - {0x8009, 0}, - {0x8017, 0}, - {0xc028, 0}, - {0x8002, 36}, - {0x8009, 36}, - {0x8017, 36}, - {0xc028, 36}, - {0x8002, 64}, - {0x8009, 64}, - {0x8017, 64}, - {0xc028, 64}, - {0x8002, 91}, - {0x8009, 91}, - {0x8017, 91}, - {0xc028, 91}, - }, - /* 86 */ - { - {0x8003, 0}, - {0x8006, 0}, - {0x800a, 0}, - {0x800f, 0}, - {0x8018, 0}, - {0x801f, 0}, - {0x8029, 0}, - {0xc038, 0}, - {0x8003, 36}, - {0x8006, 36}, - {0x800a, 36}, - {0x800f, 36}, - {0x8018, 36}, - {0x801f, 36}, - {0x8029, 36}, - {0xc038, 36}, - }, - /* 87 */ - { - {0x8003, 64}, - {0x8006, 64}, - {0x800a, 64}, - {0x800f, 64}, - {0x8018, 64}, - {0x801f, 64}, - {0x8029, 64}, - {0xc038, 64}, - {0x8003, 91}, - {0x8006, 91}, - {0x800a, 91}, - {0x800f, 91}, - {0x8018, 91}, - {0x801f, 91}, - {0x8029, 91}, - {0xc038, 91}, - }, - /* 88 */ - { - {0x8002, 93}, - {0x8009, 93}, - {0x8017, 93}, - {0xc028, 93}, - {0x8002, 126}, - {0x8009, 126}, - {0x8017, 126}, - {0xc028, 126}, - {0x8001, 94}, - {0xc016, 94}, - {0x8001, 125}, - {0xc016, 125}, - {0xc000, 60}, - {0xc000, 96}, - {0xc000, 123}, - {0x5f, 0}, - }, - /* 89 */ - { - {0x8003, 93}, - {0x8006, 93}, - {0x800a, 93}, - {0x800f, 93}, - {0x8018, 93}, - {0x801f, 93}, - {0x8029, 93}, - {0xc038, 93}, - {0x8003, 126}, - {0x8006, 126}, - {0x800a, 126}, - {0x800f, 126}, - {0x8018, 126}, - {0x801f, 126}, - {0x8029, 126}, - {0xc038, 126}, - }, - /* 90 */ - { - {0x8002, 94}, - {0x8009, 94}, - {0x8017, 94}, - {0xc028, 94}, - {0x8002, 125}, - {0x8009, 125}, - {0x8017, 125}, - {0xc028, 125}, - {0x8001, 60}, - {0xc016, 60}, - {0x8001, 96}, - {0xc016, 96}, - {0x8001, 123}, - {0xc016, 123}, - {0x60, 0}, - {0x6e, 0}, - }, - /* 91 */ - { - {0x8003, 94}, - {0x8006, 94}, - {0x800a, 94}, - {0x800f, 94}, - {0x8018, 94}, - {0x801f, 94}, - {0x8029, 94}, - {0xc038, 94}, - {0x8003, 125}, - {0x8006, 125}, - {0x800a, 125}, - {0x800f, 125}, - {0x8018, 125}, - {0x801f, 125}, - {0x8029, 125}, - {0xc038, 125}, - }, - /* 92 */ - { - {0x8002, 60}, - {0x8009, 60}, - {0x8017, 60}, - {0xc028, 60}, - {0x8002, 96}, - {0x8009, 96}, - {0x8017, 96}, - {0xc028, 96}, - {0x8002, 123}, - {0x8009, 123}, - {0x8017, 123}, - {0xc028, 123}, - {0x61, 0}, - {0x65, 0}, - {0x6f, 0}, - {0x85, 0}, - }, - /* 93 */ - { - {0x8003, 60}, - {0x8006, 60}, - {0x800a, 60}, - {0x800f, 60}, - {0x8018, 60}, - {0x801f, 60}, - {0x8029, 60}, - {0xc038, 60}, - {0x8003, 96}, - {0x8006, 96}, - {0x800a, 96}, - {0x800f, 96}, - {0x8018, 96}, - {0x801f, 96}, - {0x8029, 96}, - {0xc038, 96}, - }, - /* 94 */ - { - {0x8003, 123}, - {0x8006, 123}, - {0x800a, 123}, - {0x800f, 123}, - {0x8018, 123}, - {0x801f, 123}, - {0x8029, 123}, - {0xc038, 123}, - {0x62, 0}, - {0x63, 0}, - {0x66, 0}, - {0x69, 0}, - {0x70, 0}, - {0x77, 0}, - {0x86, 0}, - {0x99, 0}, - }, - /* 95 */ - { - {0xc000, 92}, - {0xc000, 195}, - {0xc000, 208}, - {0x64, 0}, - {0x67, 0}, - {0x68, 0}, - {0x6a, 0}, - {0x6b, 0}, - {0x71, 0}, - {0x74, 0}, - {0x78, 0}, - {0x7e, 0}, - {0x87, 0}, - {0x8e, 0}, - {0x9a, 0}, - {0xa9, 0}, - }, - /* 96 */ - { - {0x8001, 92}, - {0xc016, 92}, - {0x8001, 195}, - {0xc016, 195}, - {0x8001, 208}, - {0xc016, 208}, - {0xc000, 128}, - {0xc000, 130}, - {0xc000, 131}, - {0xc000, 162}, - {0xc000, 184}, - {0xc000, 194}, - {0xc000, 224}, - {0xc000, 226}, - {0x6c, 0}, - {0x6d, 0}, - }, - /* 97 */ - { - {0x8002, 92}, - {0x8009, 92}, - {0x8017, 92}, - {0xc028, 92}, - {0x8002, 195}, - {0x8009, 195}, - {0x8017, 195}, - {0xc028, 195}, - {0x8002, 208}, - {0x8009, 208}, - {0x8017, 208}, - {0xc028, 208}, - {0x8001, 128}, - {0xc016, 128}, - {0x8001, 130}, - {0xc016, 130}, - }, - /* 98 */ - { - {0x8003, 92}, - {0x8006, 92}, - {0x800a, 92}, - {0x800f, 92}, - {0x8018, 92}, - {0x801f, 92}, - {0x8029, 92}, - {0xc038, 92}, - {0x8003, 195}, - {0x8006, 195}, - {0x800a, 195}, - {0x800f, 195}, - {0x8018, 195}, - {0x801f, 195}, - {0x8029, 195}, - {0xc038, 195}, - }, - /* 99 */ - { - {0x8003, 208}, - {0x8006, 208}, - {0x800a, 208}, - {0x800f, 208}, - {0x8018, 208}, - {0x801f, 208}, - {0x8029, 208}, - {0xc038, 208}, - {0x8002, 128}, - {0x8009, 128}, - {0x8017, 128}, - {0xc028, 128}, - {0x8002, 130}, - {0x8009, 130}, - {0x8017, 130}, - {0xc028, 130}, - }, - /* 100 */ - { - {0x8003, 128}, - {0x8006, 128}, - {0x800a, 128}, - {0x800f, 128}, - {0x8018, 128}, - {0x801f, 128}, - {0x8029, 128}, - {0xc038, 128}, - {0x8003, 130}, - {0x8006, 130}, - {0x800a, 130}, - {0x800f, 130}, - {0x8018, 130}, - {0x801f, 130}, - {0x8029, 130}, - {0xc038, 130}, - }, - /* 101 */ - { - {0x8001, 131}, - {0xc016, 131}, - {0x8001, 162}, - {0xc016, 162}, - {0x8001, 184}, - {0xc016, 184}, - {0x8001, 194}, - {0xc016, 194}, - {0x8001, 224}, - {0xc016, 224}, - {0x8001, 226}, - {0xc016, 226}, - {0xc000, 153}, - {0xc000, 161}, - {0xc000, 167}, - {0xc000, 172}, - }, - /* 102 */ - { - {0x8002, 131}, - {0x8009, 131}, - {0x8017, 131}, - {0xc028, 131}, - {0x8002, 162}, - {0x8009, 162}, - {0x8017, 162}, - {0xc028, 162}, - {0x8002, 184}, - {0x8009, 184}, - {0x8017, 184}, - {0xc028, 184}, - {0x8002, 194}, - {0x8009, 194}, - {0x8017, 194}, - {0xc028, 194}, - }, - /* 103 */ - { - {0x8003, 131}, - {0x8006, 131}, - {0x800a, 131}, - {0x800f, 131}, - {0x8018, 131}, - {0x801f, 131}, - {0x8029, 131}, - {0xc038, 131}, - {0x8003, 162}, - {0x8006, 162}, - {0x800a, 162}, - {0x800f, 162}, - {0x8018, 162}, - {0x801f, 162}, - {0x8029, 162}, - {0xc038, 162}, - }, - /* 104 */ - { - {0x8003, 184}, - {0x8006, 184}, - {0x800a, 184}, - {0x800f, 184}, - {0x8018, 184}, - {0x801f, 184}, - {0x8029, 184}, - {0xc038, 184}, - {0x8003, 194}, - {0x8006, 194}, - {0x800a, 194}, - {0x800f, 194}, - {0x8018, 194}, - {0x801f, 194}, - {0x8029, 194}, - {0xc038, 194}, - }, - /* 105 */ - { - {0x8002, 224}, - {0x8009, 224}, - {0x8017, 224}, - {0xc028, 224}, - {0x8002, 226}, - {0x8009, 226}, - {0x8017, 226}, - {0xc028, 226}, - {0x8001, 153}, - {0xc016, 153}, - {0x8001, 161}, - {0xc016, 161}, - {0x8001, 167}, - {0xc016, 167}, - {0x8001, 172}, - {0xc016, 172}, - }, - /* 106 */ - { - {0x8003, 224}, - {0x8006, 224}, - {0x800a, 224}, - {0x800f, 224}, - {0x8018, 224}, - {0x801f, 224}, - {0x8029, 224}, - {0xc038, 224}, - {0x8003, 226}, - {0x8006, 226}, - {0x800a, 226}, - {0x800f, 226}, - {0x8018, 226}, - {0x801f, 226}, - {0x8029, 226}, - {0xc038, 226}, - }, - /* 107 */ - { - {0x8002, 153}, - {0x8009, 153}, - {0x8017, 153}, - {0xc028, 153}, - {0x8002, 161}, - {0x8009, 161}, - {0x8017, 161}, - {0xc028, 161}, - {0x8002, 167}, - {0x8009, 167}, - {0x8017, 167}, - {0xc028, 167}, - {0x8002, 172}, - {0x8009, 172}, - {0x8017, 172}, - {0xc028, 172}, - }, - /* 108 */ - { - {0x8003, 153}, - {0x8006, 153}, - {0x800a, 153}, - {0x800f, 153}, - {0x8018, 153}, - {0x801f, 153}, - {0x8029, 153}, - {0xc038, 153}, - {0x8003, 161}, - {0x8006, 161}, - {0x800a, 161}, - {0x800f, 161}, - {0x8018, 161}, - {0x801f, 161}, - {0x8029, 161}, - {0xc038, 161}, - }, - /* 109 */ - { - {0x8003, 167}, - {0x8006, 167}, - {0x800a, 167}, - {0x800f, 167}, - {0x8018, 167}, - {0x801f, 167}, - {0x8029, 167}, - {0xc038, 167}, - {0x8003, 172}, - {0x8006, 172}, - {0x800a, 172}, - {0x800f, 172}, - {0x8018, 172}, - {0x801f, 172}, - {0x8029, 172}, - {0xc038, 172}, - }, - /* 110 */ - { - {0x72, 0}, - {0x73, 0}, - {0x75, 0}, - {0x76, 0}, - {0x79, 0}, - {0x7b, 0}, - {0x7f, 0}, - {0x82, 0}, - {0x88, 0}, - {0x8b, 0}, - {0x8f, 0}, - {0x92, 0}, - {0x9b, 0}, - {0xa2, 0}, - {0xaa, 0}, - {0xb4, 0}, - }, - /* 111 */ - { - {0xc000, 176}, - {0xc000, 177}, - {0xc000, 179}, - {0xc000, 209}, - {0xc000, 216}, - {0xc000, 217}, - {0xc000, 227}, - {0xc000, 229}, - {0xc000, 230}, - {0x7a, 0}, - {0x7c, 0}, - {0x7d, 0}, - {0x80, 0}, - {0x81, 0}, - {0x83, 0}, - {0x84, 0}, - }, - /* 112 */ - { - {0x8001, 176}, - {0xc016, 176}, - {0x8001, 177}, - {0xc016, 177}, - {0x8001, 179}, - {0xc016, 179}, - {0x8001, 209}, - {0xc016, 209}, - {0x8001, 216}, - {0xc016, 216}, - {0x8001, 217}, - {0xc016, 217}, - {0x8001, 227}, - {0xc016, 227}, - {0x8001, 229}, - {0xc016, 229}, - }, - /* 113 */ - { - {0x8002, 176}, - {0x8009, 176}, - {0x8017, 176}, - {0xc028, 176}, - {0x8002, 177}, - {0x8009, 177}, - {0x8017, 177}, - {0xc028, 177}, - {0x8002, 179}, - {0x8009, 179}, - {0x8017, 179}, - {0xc028, 179}, - {0x8002, 209}, - {0x8009, 209}, - {0x8017, 209}, - {0xc028, 209}, - }, - /* 114 */ - { - {0x8003, 176}, - {0x8006, 176}, - {0x800a, 176}, - {0x800f, 176}, - {0x8018, 176}, - {0x801f, 176}, - {0x8029, 176}, - {0xc038, 176}, - {0x8003, 177}, - {0x8006, 177}, - {0x800a, 177}, - {0x800f, 177}, - {0x8018, 177}, - {0x801f, 177}, - {0x8029, 177}, - {0xc038, 177}, - }, - /* 115 */ - { - {0x8003, 179}, - {0x8006, 179}, - {0x800a, 179}, - {0x800f, 179}, - {0x8018, 179}, - {0x801f, 179}, - {0x8029, 179}, - {0xc038, 179}, - {0x8003, 209}, - {0x8006, 209}, - {0x800a, 209}, - {0x800f, 209}, - {0x8018, 209}, - {0x801f, 209}, - {0x8029, 209}, - {0xc038, 209}, - }, - /* 116 */ - { - {0x8002, 216}, - {0x8009, 216}, - {0x8017, 216}, - {0xc028, 216}, - {0x8002, 217}, - {0x8009, 217}, - {0x8017, 217}, - {0xc028, 217}, - {0x8002, 227}, - {0x8009, 227}, - {0x8017, 227}, - {0xc028, 227}, - {0x8002, 229}, - {0x8009, 229}, - {0x8017, 229}, - {0xc028, 229}, - }, - /* 117 */ - { - {0x8003, 216}, - {0x8006, 216}, - {0x800a, 216}, - {0x800f, 216}, - {0x8018, 216}, - {0x801f, 216}, - {0x8029, 216}, - {0xc038, 216}, - {0x8003, 217}, - {0x8006, 217}, - {0x800a, 217}, - {0x800f, 217}, - {0x8018, 217}, - {0x801f, 217}, - {0x8029, 217}, - {0xc038, 217}, - }, - /* 118 */ - { - {0x8003, 227}, - {0x8006, 227}, - {0x800a, 227}, - {0x800f, 227}, - {0x8018, 227}, - {0x801f, 227}, - {0x8029, 227}, - {0xc038, 227}, - {0x8003, 229}, - {0x8006, 229}, - {0x800a, 229}, - {0x800f, 229}, - {0x8018, 229}, - {0x801f, 229}, - {0x8029, 229}, - {0xc038, 229}, - }, - /* 119 */ - { - {0x8001, 230}, - {0xc016, 230}, - {0xc000, 129}, - {0xc000, 132}, - {0xc000, 133}, - {0xc000, 134}, - {0xc000, 136}, - {0xc000, 146}, - {0xc000, 154}, - {0xc000, 156}, - {0xc000, 160}, - {0xc000, 163}, - {0xc000, 164}, - {0xc000, 169}, - {0xc000, 170}, - {0xc000, 173}, - }, - /* 120 */ - { - {0x8002, 230}, - {0x8009, 230}, - {0x8017, 230}, - {0xc028, 230}, - {0x8001, 129}, - {0xc016, 129}, - {0x8001, 132}, - {0xc016, 132}, - {0x8001, 133}, - {0xc016, 133}, - {0x8001, 134}, - {0xc016, 134}, - {0x8001, 136}, - {0xc016, 136}, - {0x8001, 146}, - {0xc016, 146}, - }, - /* 121 */ - { - {0x8003, 230}, - {0x8006, 230}, - {0x800a, 230}, - {0x800f, 230}, - {0x8018, 230}, - {0x801f, 230}, - {0x8029, 230}, - {0xc038, 230}, - {0x8002, 129}, - {0x8009, 129}, - {0x8017, 129}, - {0xc028, 129}, - {0x8002, 132}, - {0x8009, 132}, - {0x8017, 132}, - {0xc028, 132}, - }, - /* 122 */ - { - {0x8003, 129}, - {0x8006, 129}, - {0x800a, 129}, - {0x800f, 129}, - {0x8018, 129}, - {0x801f, 129}, - {0x8029, 129}, - {0xc038, 129}, - {0x8003, 132}, - {0x8006, 132}, - {0x800a, 132}, - {0x800f, 132}, - {0x8018, 132}, - {0x801f, 132}, - {0x8029, 132}, - {0xc038, 132}, - }, - /* 123 */ - { - {0x8002, 133}, - {0x8009, 133}, - {0x8017, 133}, - {0xc028, 133}, - {0x8002, 134}, - {0x8009, 134}, - {0x8017, 134}, - {0xc028, 134}, - {0x8002, 136}, - {0x8009, 136}, - {0x8017, 136}, - {0xc028, 136}, - {0x8002, 146}, - {0x8009, 146}, - {0x8017, 146}, - {0xc028, 146}, - }, - /* 124 */ - { - {0x8003, 133}, - {0x8006, 133}, - {0x800a, 133}, - {0x800f, 133}, - {0x8018, 133}, - {0x801f, 133}, - {0x8029, 133}, - {0xc038, 133}, - {0x8003, 134}, - {0x8006, 134}, - {0x800a, 134}, - {0x800f, 134}, - {0x8018, 134}, - {0x801f, 134}, - {0x8029, 134}, - {0xc038, 134}, - }, - /* 125 */ - { - {0x8003, 136}, - {0x8006, 136}, - {0x800a, 136}, - {0x800f, 136}, - {0x8018, 136}, - {0x801f, 136}, - {0x8029, 136}, - {0xc038, 136}, - {0x8003, 146}, - {0x8006, 146}, - {0x800a, 146}, - {0x800f, 146}, - {0x8018, 146}, - {0x801f, 146}, - {0x8029, 146}, - {0xc038, 146}, - }, - /* 126 */ - { - {0x8001, 154}, - {0xc016, 154}, - {0x8001, 156}, - {0xc016, 156}, - {0x8001, 160}, - {0xc016, 160}, - {0x8001, 163}, - {0xc016, 163}, - {0x8001, 164}, - {0xc016, 164}, - {0x8001, 169}, - {0xc016, 169}, - {0x8001, 170}, - {0xc016, 170}, - {0x8001, 173}, - {0xc016, 173}, - }, - /* 127 */ - { - {0x8002, 154}, - {0x8009, 154}, - {0x8017, 154}, - {0xc028, 154}, - {0x8002, 156}, - {0x8009, 156}, - {0x8017, 156}, - {0xc028, 156}, - {0x8002, 160}, - {0x8009, 160}, - {0x8017, 160}, - {0xc028, 160}, - {0x8002, 163}, - {0x8009, 163}, - {0x8017, 163}, - {0xc028, 163}, - }, - /* 128 */ - { - {0x8003, 154}, - {0x8006, 154}, - {0x800a, 154}, - {0x800f, 154}, - {0x8018, 154}, - {0x801f, 154}, - {0x8029, 154}, - {0xc038, 154}, - {0x8003, 156}, - {0x8006, 156}, - {0x800a, 156}, - {0x800f, 156}, - {0x8018, 156}, - {0x801f, 156}, - {0x8029, 156}, - {0xc038, 156}, - }, - /* 129 */ - { - {0x8003, 160}, - {0x8006, 160}, - {0x800a, 160}, - {0x800f, 160}, - {0x8018, 160}, - {0x801f, 160}, - {0x8029, 160}, - {0xc038, 160}, - {0x8003, 163}, - {0x8006, 163}, - {0x800a, 163}, - {0x800f, 163}, - {0x8018, 163}, - {0x801f, 163}, - {0x8029, 163}, - {0xc038, 163}, - }, - /* 130 */ - { - {0x8002, 164}, - {0x8009, 164}, - {0x8017, 164}, - {0xc028, 164}, - {0x8002, 169}, - {0x8009, 169}, - {0x8017, 169}, - {0xc028, 169}, - {0x8002, 170}, - {0x8009, 170}, - {0x8017, 170}, - {0xc028, 170}, - {0x8002, 173}, - {0x8009, 173}, - {0x8017, 173}, - {0xc028, 173}, - }, - /* 131 */ - { - {0x8003, 164}, - {0x8006, 164}, - {0x800a, 164}, - {0x800f, 164}, - {0x8018, 164}, - {0x801f, 164}, - {0x8029, 164}, - {0xc038, 164}, - {0x8003, 169}, - {0x8006, 169}, - {0x800a, 169}, - {0x800f, 169}, - {0x8018, 169}, - {0x801f, 169}, - {0x8029, 169}, - {0xc038, 169}, - }, - /* 132 */ - { - {0x8003, 170}, - {0x8006, 170}, - {0x800a, 170}, - {0x800f, 170}, - {0x8018, 170}, - {0x801f, 170}, - {0x8029, 170}, - {0xc038, 170}, - {0x8003, 173}, - {0x8006, 173}, - {0x800a, 173}, - {0x800f, 173}, - {0x8018, 173}, - {0x801f, 173}, - {0x8029, 173}, - {0xc038, 173}, - }, - /* 133 */ - { - {0x89, 0}, - {0x8a, 0}, - {0x8c, 0}, - {0x8d, 0}, - {0x90, 0}, - {0x91, 0}, - {0x93, 0}, - {0x96, 0}, - {0x9c, 0}, - {0x9f, 0}, - {0xa3, 0}, - {0xa6, 0}, - {0xab, 0}, - {0xae, 0}, - {0xb5, 0}, - {0xbe, 0}, - }, - /* 134 */ - { - {0xc000, 178}, - {0xc000, 181}, - {0xc000, 185}, - {0xc000, 186}, - {0xc000, 187}, - {0xc000, 189}, - {0xc000, 190}, - {0xc000, 196}, - {0xc000, 198}, - {0xc000, 228}, - {0xc000, 232}, - {0xc000, 233}, - {0x94, 0}, - {0x95, 0}, - {0x97, 0}, - {0x98, 0}, - }, - /* 135 */ - { - {0x8001, 178}, - {0xc016, 178}, - {0x8001, 181}, - {0xc016, 181}, - {0x8001, 185}, - {0xc016, 185}, - {0x8001, 186}, - {0xc016, 186}, - {0x8001, 187}, - {0xc016, 187}, - {0x8001, 189}, - {0xc016, 189}, - {0x8001, 190}, - {0xc016, 190}, - {0x8001, 196}, - {0xc016, 196}, - }, - /* 136 */ - { - {0x8002, 178}, - {0x8009, 178}, - {0x8017, 178}, - {0xc028, 178}, - {0x8002, 181}, - {0x8009, 181}, - {0x8017, 181}, - {0xc028, 181}, - {0x8002, 185}, - {0x8009, 185}, - {0x8017, 185}, - {0xc028, 185}, - {0x8002, 186}, - {0x8009, 186}, - {0x8017, 186}, - {0xc028, 186}, - }, - /* 137 */ - { - {0x8003, 178}, - {0x8006, 178}, - {0x800a, 178}, - {0x800f, 178}, - {0x8018, 178}, - {0x801f, 178}, - {0x8029, 178}, - {0xc038, 178}, - {0x8003, 181}, - {0x8006, 181}, - {0x800a, 181}, - {0x800f, 181}, - {0x8018, 181}, - {0x801f, 181}, - {0x8029, 181}, - {0xc038, 181}, - }, - /* 138 */ - { - {0x8003, 185}, - {0x8006, 185}, - {0x800a, 185}, - {0x800f, 185}, - {0x8018, 185}, - {0x801f, 185}, - {0x8029, 185}, - {0xc038, 185}, - {0x8003, 186}, - {0x8006, 186}, - {0x800a, 186}, - {0x800f, 186}, - {0x8018, 186}, - {0x801f, 186}, - {0x8029, 186}, - {0xc038, 186}, - }, - /* 139 */ - { - {0x8002, 187}, - {0x8009, 187}, - {0x8017, 187}, - {0xc028, 187}, - {0x8002, 189}, - {0x8009, 189}, - {0x8017, 189}, - {0xc028, 189}, - {0x8002, 190}, - {0x8009, 190}, - {0x8017, 190}, - {0xc028, 190}, - {0x8002, 196}, - {0x8009, 196}, - {0x8017, 196}, - {0xc028, 196}, - }, - /* 140 */ - { - {0x8003, 187}, - {0x8006, 187}, - {0x800a, 187}, - {0x800f, 187}, - {0x8018, 187}, - {0x801f, 187}, - {0x8029, 187}, - {0xc038, 187}, - {0x8003, 189}, - {0x8006, 189}, - {0x800a, 189}, - {0x800f, 189}, - {0x8018, 189}, - {0x801f, 189}, - {0x8029, 189}, - {0xc038, 189}, - }, - /* 141 */ - { - {0x8003, 190}, - {0x8006, 190}, - {0x800a, 190}, - {0x800f, 190}, - {0x8018, 190}, - {0x801f, 190}, - {0x8029, 190}, - {0xc038, 190}, - {0x8003, 196}, - {0x8006, 196}, - {0x800a, 196}, - {0x800f, 196}, - {0x8018, 196}, - {0x801f, 196}, - {0x8029, 196}, - {0xc038, 196}, - }, - /* 142 */ - { - {0x8001, 198}, - {0xc016, 198}, - {0x8001, 228}, - {0xc016, 228}, - {0x8001, 232}, - {0xc016, 232}, - {0x8001, 233}, - {0xc016, 233}, - {0xc000, 1}, - {0xc000, 135}, - {0xc000, 137}, - {0xc000, 138}, - {0xc000, 139}, - {0xc000, 140}, - {0xc000, 141}, - {0xc000, 143}, - }, - /* 143 */ - { - {0x8002, 198}, - {0x8009, 198}, - {0x8017, 198}, - {0xc028, 198}, - {0x8002, 228}, - {0x8009, 228}, - {0x8017, 228}, - {0xc028, 228}, - {0x8002, 232}, - {0x8009, 232}, - {0x8017, 232}, - {0xc028, 232}, - {0x8002, 233}, - {0x8009, 233}, - {0x8017, 233}, - {0xc028, 233}, - }, - /* 144 */ - { - {0x8003, 198}, - {0x8006, 198}, - {0x800a, 198}, - {0x800f, 198}, - {0x8018, 198}, - {0x801f, 198}, - {0x8029, 198}, - {0xc038, 198}, - {0x8003, 228}, - {0x8006, 228}, - {0x800a, 228}, - {0x800f, 228}, - {0x8018, 228}, - {0x801f, 228}, - {0x8029, 228}, - {0xc038, 228}, - }, - /* 145 */ - { - {0x8003, 232}, - {0x8006, 232}, - {0x800a, 232}, - {0x800f, 232}, - {0x8018, 232}, - {0x801f, 232}, - {0x8029, 232}, - {0xc038, 232}, - {0x8003, 233}, - {0x8006, 233}, - {0x800a, 233}, - {0x800f, 233}, - {0x8018, 233}, - {0x801f, 233}, - {0x8029, 233}, - {0xc038, 233}, - }, - /* 146 */ - { - {0x8001, 1}, - {0xc016, 1}, - {0x8001, 135}, - {0xc016, 135}, - {0x8001, 137}, - {0xc016, 137}, - {0x8001, 138}, - {0xc016, 138}, - {0x8001, 139}, - {0xc016, 139}, - {0x8001, 140}, - {0xc016, 140}, - {0x8001, 141}, - {0xc016, 141}, - {0x8001, 143}, - {0xc016, 143}, - }, - /* 147 */ - { - {0x8002, 1}, - {0x8009, 1}, - {0x8017, 1}, - {0xc028, 1}, - {0x8002, 135}, - {0x8009, 135}, - {0x8017, 135}, - {0xc028, 135}, - {0x8002, 137}, - {0x8009, 137}, - {0x8017, 137}, - {0xc028, 137}, - {0x8002, 138}, - {0x8009, 138}, - {0x8017, 138}, - {0xc028, 138}, - }, - /* 148 */ - { - {0x8003, 1}, - {0x8006, 1}, - {0x800a, 1}, - {0x800f, 1}, - {0x8018, 1}, - {0x801f, 1}, - {0x8029, 1}, - {0xc038, 1}, - {0x8003, 135}, - {0x8006, 135}, - {0x800a, 135}, - {0x800f, 135}, - {0x8018, 135}, - {0x801f, 135}, - {0x8029, 135}, - {0xc038, 135}, - }, - /* 149 */ - { - {0x8003, 137}, - {0x8006, 137}, - {0x800a, 137}, - {0x800f, 137}, - {0x8018, 137}, - {0x801f, 137}, - {0x8029, 137}, - {0xc038, 137}, - {0x8003, 138}, - {0x8006, 138}, - {0x800a, 138}, - {0x800f, 138}, - {0x8018, 138}, - {0x801f, 138}, - {0x8029, 138}, - {0xc038, 138}, - }, - /* 150 */ - { - {0x8002, 139}, - {0x8009, 139}, - {0x8017, 139}, - {0xc028, 139}, - {0x8002, 140}, - {0x8009, 140}, - {0x8017, 140}, - {0xc028, 140}, - {0x8002, 141}, - {0x8009, 141}, - {0x8017, 141}, - {0xc028, 141}, - {0x8002, 143}, - {0x8009, 143}, - {0x8017, 143}, - {0xc028, 143}, - }, - /* 151 */ - { - {0x8003, 139}, - {0x8006, 139}, - {0x800a, 139}, - {0x800f, 139}, - {0x8018, 139}, - {0x801f, 139}, - {0x8029, 139}, - {0xc038, 139}, - {0x8003, 140}, - {0x8006, 140}, - {0x800a, 140}, - {0x800f, 140}, - {0x8018, 140}, - {0x801f, 140}, - {0x8029, 140}, - {0xc038, 140}, - }, - /* 152 */ - { - {0x8003, 141}, - {0x8006, 141}, - {0x800a, 141}, - {0x800f, 141}, - {0x8018, 141}, - {0x801f, 141}, - {0x8029, 141}, - {0xc038, 141}, - {0x8003, 143}, - {0x8006, 143}, - {0x800a, 143}, - {0x800f, 143}, - {0x8018, 143}, - {0x801f, 143}, - {0x8029, 143}, - {0xc038, 143}, - }, - /* 153 */ - { - {0x9d, 0}, - {0x9e, 0}, - {0xa0, 0}, - {0xa1, 0}, - {0xa4, 0}, - {0xa5, 0}, - {0xa7, 0}, - {0xa8, 0}, - {0xac, 0}, - {0xad, 0}, - {0xaf, 0}, - {0xb1, 0}, - {0xb6, 0}, - {0xb9, 0}, - {0xbf, 0}, - {0xcf, 0}, - }, - /* 154 */ - { - {0xc000, 147}, - {0xc000, 149}, - {0xc000, 150}, - {0xc000, 151}, - {0xc000, 152}, - {0xc000, 155}, - {0xc000, 157}, - {0xc000, 158}, - {0xc000, 165}, - {0xc000, 166}, - {0xc000, 168}, - {0xc000, 174}, - {0xc000, 175}, - {0xc000, 180}, - {0xc000, 182}, - {0xc000, 183}, - }, - /* 155 */ - { - {0x8001, 147}, - {0xc016, 147}, - {0x8001, 149}, - {0xc016, 149}, - {0x8001, 150}, - {0xc016, 150}, - {0x8001, 151}, - {0xc016, 151}, - {0x8001, 152}, - {0xc016, 152}, - {0x8001, 155}, - {0xc016, 155}, - {0x8001, 157}, - {0xc016, 157}, - {0x8001, 158}, - {0xc016, 158}, - }, - /* 156 */ - { - {0x8002, 147}, - {0x8009, 147}, - {0x8017, 147}, - {0xc028, 147}, - {0x8002, 149}, - {0x8009, 149}, - {0x8017, 149}, - {0xc028, 149}, - {0x8002, 150}, - {0x8009, 150}, - {0x8017, 150}, - {0xc028, 150}, - {0x8002, 151}, - {0x8009, 151}, - {0x8017, 151}, - {0xc028, 151}, - }, - /* 157 */ - { - {0x8003, 147}, - {0x8006, 147}, - {0x800a, 147}, - {0x800f, 147}, - {0x8018, 147}, - {0x801f, 147}, - {0x8029, 147}, - {0xc038, 147}, - {0x8003, 149}, - {0x8006, 149}, - {0x800a, 149}, - {0x800f, 149}, - {0x8018, 149}, - {0x801f, 149}, - {0x8029, 149}, - {0xc038, 149}, - }, - /* 158 */ - { - {0x8003, 150}, - {0x8006, 150}, - {0x800a, 150}, - {0x800f, 150}, - {0x8018, 150}, - {0x801f, 150}, - {0x8029, 150}, - {0xc038, 150}, - {0x8003, 151}, - {0x8006, 151}, - {0x800a, 151}, - {0x800f, 151}, - {0x8018, 151}, - {0x801f, 151}, - {0x8029, 151}, - {0xc038, 151}, - }, - /* 159 */ - { - {0x8002, 152}, - {0x8009, 152}, - {0x8017, 152}, - {0xc028, 152}, - {0x8002, 155}, - {0x8009, 155}, - {0x8017, 155}, - {0xc028, 155}, - {0x8002, 157}, - {0x8009, 157}, - {0x8017, 157}, - {0xc028, 157}, - {0x8002, 158}, - {0x8009, 158}, - {0x8017, 158}, - {0xc028, 158}, - }, - /* 160 */ - { - {0x8003, 152}, - {0x8006, 152}, - {0x800a, 152}, - {0x800f, 152}, - {0x8018, 152}, - {0x801f, 152}, - {0x8029, 152}, - {0xc038, 152}, - {0x8003, 155}, - {0x8006, 155}, - {0x800a, 155}, - {0x800f, 155}, - {0x8018, 155}, - {0x801f, 155}, - {0x8029, 155}, - {0xc038, 155}, - }, - /* 161 */ - { - {0x8003, 157}, - {0x8006, 157}, - {0x800a, 157}, - {0x800f, 157}, - {0x8018, 157}, - {0x801f, 157}, - {0x8029, 157}, - {0xc038, 157}, - {0x8003, 158}, - {0x8006, 158}, - {0x800a, 158}, - {0x800f, 158}, - {0x8018, 158}, - {0x801f, 158}, - {0x8029, 158}, - {0xc038, 158}, - }, - /* 162 */ - { - {0x8001, 165}, - {0xc016, 165}, - {0x8001, 166}, - {0xc016, 166}, - {0x8001, 168}, - {0xc016, 168}, - {0x8001, 174}, - {0xc016, 174}, - {0x8001, 175}, - {0xc016, 175}, - {0x8001, 180}, - {0xc016, 180}, - {0x8001, 182}, - {0xc016, 182}, - {0x8001, 183}, - {0xc016, 183}, - }, - /* 163 */ - { - {0x8002, 165}, - {0x8009, 165}, - {0x8017, 165}, - {0xc028, 165}, - {0x8002, 166}, - {0x8009, 166}, - {0x8017, 166}, - {0xc028, 166}, - {0x8002, 168}, - {0x8009, 168}, - {0x8017, 168}, - {0xc028, 168}, - {0x8002, 174}, - {0x8009, 174}, - {0x8017, 174}, - {0xc028, 174}, - }, - /* 164 */ - { - {0x8003, 165}, - {0x8006, 165}, - {0x800a, 165}, - {0x800f, 165}, - {0x8018, 165}, - {0x801f, 165}, - {0x8029, 165}, - {0xc038, 165}, - {0x8003, 166}, - {0x8006, 166}, - {0x800a, 166}, - {0x800f, 166}, - {0x8018, 166}, - {0x801f, 166}, - {0x8029, 166}, - {0xc038, 166}, - }, - /* 165 */ - { - {0x8003, 168}, - {0x8006, 168}, - {0x800a, 168}, - {0x800f, 168}, - {0x8018, 168}, - {0x801f, 168}, - {0x8029, 168}, - {0xc038, 168}, - {0x8003, 174}, - {0x8006, 174}, - {0x800a, 174}, - {0x800f, 174}, - {0x8018, 174}, - {0x801f, 174}, - {0x8029, 174}, - {0xc038, 174}, - }, - /* 166 */ - { - {0x8002, 175}, - {0x8009, 175}, - {0x8017, 175}, - {0xc028, 175}, - {0x8002, 180}, - {0x8009, 180}, - {0x8017, 180}, - {0xc028, 180}, - {0x8002, 182}, - {0x8009, 182}, - {0x8017, 182}, - {0xc028, 182}, - {0x8002, 183}, - {0x8009, 183}, - {0x8017, 183}, - {0xc028, 183}, - }, - /* 167 */ - { - {0x8003, 175}, - {0x8006, 175}, - {0x800a, 175}, - {0x800f, 175}, - {0x8018, 175}, - {0x801f, 175}, - {0x8029, 175}, - {0xc038, 175}, - {0x8003, 180}, - {0x8006, 180}, - {0x800a, 180}, - {0x800f, 180}, - {0x8018, 180}, - {0x801f, 180}, - {0x8029, 180}, - {0xc038, 180}, - }, - /* 168 */ - { - {0x8003, 182}, - {0x8006, 182}, - {0x800a, 182}, - {0x800f, 182}, - {0x8018, 182}, - {0x801f, 182}, - {0x8029, 182}, - {0xc038, 182}, - {0x8003, 183}, - {0x8006, 183}, - {0x800a, 183}, - {0x800f, 183}, - {0x8018, 183}, - {0x801f, 183}, - {0x8029, 183}, - {0xc038, 183}, - }, - /* 169 */ - { - {0xc000, 188}, - {0xc000, 191}, - {0xc000, 197}, - {0xc000, 231}, - {0xc000, 239}, - {0xb0, 0}, - {0xb2, 0}, - {0xb3, 0}, - {0xb7, 0}, - {0xb8, 0}, - {0xba, 0}, - {0xbb, 0}, - {0xc0, 0}, - {0xc7, 0}, - {0xd0, 0}, - {0xdf, 0}, - }, - /* 170 */ - { - {0x8001, 188}, - {0xc016, 188}, - {0x8001, 191}, - {0xc016, 191}, - {0x8001, 197}, - {0xc016, 197}, - {0x8001, 231}, - {0xc016, 231}, - {0x8001, 239}, - {0xc016, 239}, - {0xc000, 9}, - {0xc000, 142}, - {0xc000, 144}, - {0xc000, 145}, - {0xc000, 148}, - {0xc000, 159}, - }, - /* 171 */ - { - {0x8002, 188}, - {0x8009, 188}, - {0x8017, 188}, - {0xc028, 188}, - {0x8002, 191}, - {0x8009, 191}, - {0x8017, 191}, - {0xc028, 191}, - {0x8002, 197}, - {0x8009, 197}, - {0x8017, 197}, - {0xc028, 197}, - {0x8002, 231}, - {0x8009, 231}, - {0x8017, 231}, - {0xc028, 231}, - }, - /* 172 */ - { - {0x8003, 188}, - {0x8006, 188}, - {0x800a, 188}, - {0x800f, 188}, - {0x8018, 188}, - {0x801f, 188}, - {0x8029, 188}, - {0xc038, 188}, - {0x8003, 191}, - {0x8006, 191}, - {0x800a, 191}, - {0x800f, 191}, - {0x8018, 191}, - {0x801f, 191}, - {0x8029, 191}, - {0xc038, 191}, - }, - /* 173 */ - { - {0x8003, 197}, - {0x8006, 197}, - {0x800a, 197}, - {0x800f, 197}, - {0x8018, 197}, - {0x801f, 197}, - {0x8029, 197}, - {0xc038, 197}, - {0x8003, 231}, - {0x8006, 231}, - {0x800a, 231}, - {0x800f, 231}, - {0x8018, 231}, - {0x801f, 231}, - {0x8029, 231}, - {0xc038, 231}, - }, - /* 174 */ - { - {0x8002, 239}, - {0x8009, 239}, - {0x8017, 239}, - {0xc028, 239}, - {0x8001, 9}, - {0xc016, 9}, - {0x8001, 142}, - {0xc016, 142}, - {0x8001, 144}, - {0xc016, 144}, - {0x8001, 145}, - {0xc016, 145}, - {0x8001, 148}, - {0xc016, 148}, - {0x8001, 159}, - {0xc016, 159}, - }, - /* 175 */ - { - {0x8003, 239}, - {0x8006, 239}, - {0x800a, 239}, - {0x800f, 239}, - {0x8018, 239}, - {0x801f, 239}, - {0x8029, 239}, - {0xc038, 239}, - {0x8002, 9}, - {0x8009, 9}, - {0x8017, 9}, - {0xc028, 9}, - {0x8002, 142}, - {0x8009, 142}, - {0x8017, 142}, - {0xc028, 142}, - }, - /* 176 */ - { - {0x8003, 9}, - {0x8006, 9}, - {0x800a, 9}, - {0x800f, 9}, - {0x8018, 9}, - {0x801f, 9}, - {0x8029, 9}, - {0xc038, 9}, - {0x8003, 142}, - {0x8006, 142}, - {0x800a, 142}, - {0x800f, 142}, - {0x8018, 142}, - {0x801f, 142}, - {0x8029, 142}, - {0xc038, 142}, - }, - /* 177 */ - { - {0x8002, 144}, - {0x8009, 144}, - {0x8017, 144}, - {0xc028, 144}, - {0x8002, 145}, - {0x8009, 145}, - {0x8017, 145}, - {0xc028, 145}, - {0x8002, 148}, - {0x8009, 148}, - {0x8017, 148}, - {0xc028, 148}, - {0x8002, 159}, - {0x8009, 159}, - {0x8017, 159}, - {0xc028, 159}, - }, - /* 178 */ - { - {0x8003, 144}, - {0x8006, 144}, - {0x800a, 144}, - {0x800f, 144}, - {0x8018, 144}, - {0x801f, 144}, - {0x8029, 144}, - {0xc038, 144}, - {0x8003, 145}, - {0x8006, 145}, - {0x800a, 145}, - {0x800f, 145}, - {0x8018, 145}, - {0x801f, 145}, - {0x8029, 145}, - {0xc038, 145}, - }, - /* 179 */ - { - {0x8003, 148}, - {0x8006, 148}, - {0x800a, 148}, - {0x800f, 148}, - {0x8018, 148}, - {0x801f, 148}, - {0x8029, 148}, - {0xc038, 148}, - {0x8003, 159}, - {0x8006, 159}, - {0x800a, 159}, - {0x800f, 159}, - {0x8018, 159}, - {0x801f, 159}, - {0x8029, 159}, - {0xc038, 159}, - }, - /* 180 */ - { - {0xc000, 171}, - {0xc000, 206}, - {0xc000, 215}, - {0xc000, 225}, - {0xc000, 236}, - {0xc000, 237}, - {0xbc, 0}, - {0xbd, 0}, - {0xc1, 0}, - {0xc4, 0}, - {0xc8, 0}, - {0xcb, 0}, - {0xd1, 0}, - {0xd8, 0}, - {0xe0, 0}, - {0xee, 0}, - }, - /* 181 */ - { - {0x8001, 171}, - {0xc016, 171}, - {0x8001, 206}, - {0xc016, 206}, - {0x8001, 215}, - {0xc016, 215}, - {0x8001, 225}, - {0xc016, 225}, - {0x8001, 236}, - {0xc016, 236}, - {0x8001, 237}, - {0xc016, 237}, - {0xc000, 199}, - {0xc000, 207}, - {0xc000, 234}, - {0xc000, 235}, - }, - /* 182 */ - { - {0x8002, 171}, - {0x8009, 171}, - {0x8017, 171}, - {0xc028, 171}, - {0x8002, 206}, - {0x8009, 206}, - {0x8017, 206}, - {0xc028, 206}, - {0x8002, 215}, - {0x8009, 215}, - {0x8017, 215}, - {0xc028, 215}, - {0x8002, 225}, - {0x8009, 225}, - {0x8017, 225}, - {0xc028, 225}, - }, - /* 183 */ - { - {0x8003, 171}, - {0x8006, 171}, - {0x800a, 171}, - {0x800f, 171}, - {0x8018, 171}, - {0x801f, 171}, - {0x8029, 171}, - {0xc038, 171}, - {0x8003, 206}, - {0x8006, 206}, - {0x800a, 206}, - {0x800f, 206}, - {0x8018, 206}, - {0x801f, 206}, - {0x8029, 206}, - {0xc038, 206}, - }, - /* 184 */ - { - {0x8003, 215}, - {0x8006, 215}, - {0x800a, 215}, - {0x800f, 215}, - {0x8018, 215}, - {0x801f, 215}, - {0x8029, 215}, - {0xc038, 215}, - {0x8003, 225}, - {0x8006, 225}, - {0x800a, 225}, - {0x800f, 225}, - {0x8018, 225}, - {0x801f, 225}, - {0x8029, 225}, - {0xc038, 225}, - }, - /* 185 */ - { - {0x8002, 236}, - {0x8009, 236}, - {0x8017, 236}, - {0xc028, 236}, - {0x8002, 237}, - {0x8009, 237}, - {0x8017, 237}, - {0xc028, 237}, - {0x8001, 199}, - {0xc016, 199}, - {0x8001, 207}, - {0xc016, 207}, - {0x8001, 234}, - {0xc016, 234}, - {0x8001, 235}, - {0xc016, 235}, - }, - /* 186 */ - { - {0x8003, 236}, - {0x8006, 236}, - {0x800a, 236}, - {0x800f, 236}, - {0x8018, 236}, - {0x801f, 236}, - {0x8029, 236}, - {0xc038, 236}, - {0x8003, 237}, - {0x8006, 237}, - {0x800a, 237}, - {0x800f, 237}, - {0x8018, 237}, - {0x801f, 237}, - {0x8029, 237}, - {0xc038, 237}, - }, - /* 187 */ - { - {0x8002, 199}, - {0x8009, 199}, - {0x8017, 199}, - {0xc028, 199}, - {0x8002, 207}, - {0x8009, 207}, - {0x8017, 207}, - {0xc028, 207}, - {0x8002, 234}, - {0x8009, 234}, - {0x8017, 234}, - {0xc028, 234}, - {0x8002, 235}, - {0x8009, 235}, - {0x8017, 235}, - {0xc028, 235}, - }, - /* 188 */ - { - {0x8003, 199}, - {0x8006, 199}, - {0x800a, 199}, - {0x800f, 199}, - {0x8018, 199}, - {0x801f, 199}, - {0x8029, 199}, - {0xc038, 199}, - {0x8003, 207}, - {0x8006, 207}, - {0x800a, 207}, - {0x800f, 207}, - {0x8018, 207}, - {0x801f, 207}, - {0x8029, 207}, - {0xc038, 207}, - }, - /* 189 */ - { - {0x8003, 234}, - {0x8006, 234}, - {0x800a, 234}, - {0x800f, 234}, - {0x8018, 234}, - {0x801f, 234}, - {0x8029, 234}, - {0xc038, 234}, - {0x8003, 235}, - {0x8006, 235}, - {0x800a, 235}, - {0x800f, 235}, - {0x8018, 235}, - {0x801f, 235}, - {0x8029, 235}, - {0xc038, 235}, - }, - /* 190 */ - { - {0xc2, 0}, - {0xc3, 0}, - {0xc5, 0}, - {0xc6, 0}, - {0xc9, 0}, - {0xca, 0}, - {0xcc, 0}, - {0xcd, 0}, - {0xd2, 0}, - {0xd5, 0}, - {0xd9, 0}, - {0xdc, 0}, - {0xe1, 0}, - {0xe7, 0}, - {0xef, 0}, - {0xf6, 0}, - }, - /* 191 */ - { - {0xc000, 192}, - {0xc000, 193}, - {0xc000, 200}, - {0xc000, 201}, - {0xc000, 202}, - {0xc000, 205}, - {0xc000, 210}, - {0xc000, 213}, - {0xc000, 218}, - {0xc000, 219}, - {0xc000, 238}, - {0xc000, 240}, - {0xc000, 242}, - {0xc000, 243}, - {0xc000, 255}, - {0xce, 0}, - }, - /* 192 */ - { - {0x8001, 192}, - {0xc016, 192}, - {0x8001, 193}, - {0xc016, 193}, - {0x8001, 200}, - {0xc016, 200}, - {0x8001, 201}, - {0xc016, 201}, - {0x8001, 202}, - {0xc016, 202}, - {0x8001, 205}, - {0xc016, 205}, - {0x8001, 210}, - {0xc016, 210}, - {0x8001, 213}, - {0xc016, 213}, - }, - /* 193 */ - { - {0x8002, 192}, - {0x8009, 192}, - {0x8017, 192}, - {0xc028, 192}, - {0x8002, 193}, - {0x8009, 193}, - {0x8017, 193}, - {0xc028, 193}, - {0x8002, 200}, - {0x8009, 200}, - {0x8017, 200}, - {0xc028, 200}, - {0x8002, 201}, - {0x8009, 201}, - {0x8017, 201}, - {0xc028, 201}, - }, - /* 194 */ - { - {0x8003, 192}, - {0x8006, 192}, - {0x800a, 192}, - {0x800f, 192}, - {0x8018, 192}, - {0x801f, 192}, - {0x8029, 192}, - {0xc038, 192}, - {0x8003, 193}, - {0x8006, 193}, - {0x800a, 193}, - {0x800f, 193}, - {0x8018, 193}, - {0x801f, 193}, - {0x8029, 193}, - {0xc038, 193}, - }, - /* 195 */ - { - {0x8003, 200}, - {0x8006, 200}, - {0x800a, 200}, - {0x800f, 200}, - {0x8018, 200}, - {0x801f, 200}, - {0x8029, 200}, - {0xc038, 200}, - {0x8003, 201}, - {0x8006, 201}, - {0x800a, 201}, - {0x800f, 201}, - {0x8018, 201}, - {0x801f, 201}, - {0x8029, 201}, - {0xc038, 201}, - }, - /* 196 */ - { - {0x8002, 202}, - {0x8009, 202}, - {0x8017, 202}, - {0xc028, 202}, - {0x8002, 205}, - {0x8009, 205}, - {0x8017, 205}, - {0xc028, 205}, - {0x8002, 210}, - {0x8009, 210}, - {0x8017, 210}, - {0xc028, 210}, - {0x8002, 213}, - {0x8009, 213}, - {0x8017, 213}, - {0xc028, 213}, - }, - /* 197 */ - { - {0x8003, 202}, - {0x8006, 202}, - {0x800a, 202}, - {0x800f, 202}, - {0x8018, 202}, - {0x801f, 202}, - {0x8029, 202}, - {0xc038, 202}, - {0x8003, 205}, - {0x8006, 205}, - {0x800a, 205}, - {0x800f, 205}, - {0x8018, 205}, - {0x801f, 205}, - {0x8029, 205}, - {0xc038, 205}, - }, - /* 198 */ - { - {0x8003, 210}, - {0x8006, 210}, - {0x800a, 210}, - {0x800f, 210}, - {0x8018, 210}, - {0x801f, 210}, - {0x8029, 210}, - {0xc038, 210}, - {0x8003, 213}, - {0x8006, 213}, - {0x800a, 213}, - {0x800f, 213}, - {0x8018, 213}, - {0x801f, 213}, - {0x8029, 213}, - {0xc038, 213}, - }, - /* 199 */ - { - {0x8001, 218}, - {0xc016, 218}, - {0x8001, 219}, - {0xc016, 219}, - {0x8001, 238}, - {0xc016, 238}, - {0x8001, 240}, - {0xc016, 240}, - {0x8001, 242}, - {0xc016, 242}, - {0x8001, 243}, - {0xc016, 243}, - {0x8001, 255}, - {0xc016, 255}, - {0xc000, 203}, - {0xc000, 204}, - }, - /* 200 */ - { - {0x8002, 218}, - {0x8009, 218}, - {0x8017, 218}, - {0xc028, 218}, - {0x8002, 219}, - {0x8009, 219}, - {0x8017, 219}, - {0xc028, 219}, - {0x8002, 238}, - {0x8009, 238}, - {0x8017, 238}, - {0xc028, 238}, - {0x8002, 240}, - {0x8009, 240}, - {0x8017, 240}, - {0xc028, 240}, - }, - /* 201 */ - { - {0x8003, 218}, - {0x8006, 218}, - {0x800a, 218}, - {0x800f, 218}, - {0x8018, 218}, - {0x801f, 218}, - {0x8029, 218}, - {0xc038, 218}, - {0x8003, 219}, - {0x8006, 219}, - {0x800a, 219}, - {0x800f, 219}, - {0x8018, 219}, - {0x801f, 219}, - {0x8029, 219}, - {0xc038, 219}, - }, - /* 202 */ - { - {0x8003, 238}, - {0x8006, 238}, - {0x800a, 238}, - {0x800f, 238}, - {0x8018, 238}, - {0x801f, 238}, - {0x8029, 238}, - {0xc038, 238}, - {0x8003, 240}, - {0x8006, 240}, - {0x800a, 240}, - {0x800f, 240}, - {0x8018, 240}, - {0x801f, 240}, - {0x8029, 240}, - {0xc038, 240}, - }, - /* 203 */ - { - {0x8002, 242}, - {0x8009, 242}, - {0x8017, 242}, - {0xc028, 242}, - {0x8002, 243}, - {0x8009, 243}, - {0x8017, 243}, - {0xc028, 243}, - {0x8002, 255}, - {0x8009, 255}, - {0x8017, 255}, - {0xc028, 255}, - {0x8001, 203}, - {0xc016, 203}, - {0x8001, 204}, - {0xc016, 204}, - }, - /* 204 */ - { - {0x8003, 242}, - {0x8006, 242}, - {0x800a, 242}, - {0x800f, 242}, - {0x8018, 242}, - {0x801f, 242}, - {0x8029, 242}, - {0xc038, 242}, - {0x8003, 243}, - {0x8006, 243}, - {0x800a, 243}, - {0x800f, 243}, - {0x8018, 243}, - {0x801f, 243}, - {0x8029, 243}, - {0xc038, 243}, - }, - /* 205 */ - { - {0x8003, 255}, - {0x8006, 255}, - {0x800a, 255}, - {0x800f, 255}, - {0x8018, 255}, - {0x801f, 255}, - {0x8029, 255}, - {0xc038, 255}, - {0x8002, 203}, - {0x8009, 203}, - {0x8017, 203}, - {0xc028, 203}, - {0x8002, 204}, - {0x8009, 204}, - {0x8017, 204}, - {0xc028, 204}, - }, - /* 206 */ - { - {0x8003, 203}, - {0x8006, 203}, - {0x800a, 203}, - {0x800f, 203}, - {0x8018, 203}, - {0x801f, 203}, - {0x8029, 203}, - {0xc038, 203}, - {0x8003, 204}, - {0x8006, 204}, - {0x800a, 204}, - {0x800f, 204}, - {0x8018, 204}, - {0x801f, 204}, - {0x8029, 204}, - {0xc038, 204}, - }, - /* 207 */ - { - {0xd3, 0}, - {0xd4, 0}, - {0xd6, 0}, - {0xd7, 0}, - {0xda, 0}, - {0xdb, 0}, - {0xdd, 0}, - {0xde, 0}, - {0xe2, 0}, - {0xe4, 0}, - {0xe8, 0}, - {0xeb, 0}, - {0xf0, 0}, - {0xf3, 0}, - {0xf7, 0}, - {0xfa, 0}, - }, - /* 208 */ - { - {0xc000, 211}, - {0xc000, 212}, - {0xc000, 214}, - {0xc000, 221}, - {0xc000, 222}, - {0xc000, 223}, - {0xc000, 241}, - {0xc000, 244}, - {0xc000, 245}, - {0xc000, 246}, - {0xc000, 247}, - {0xc000, 248}, - {0xc000, 250}, - {0xc000, 251}, - {0xc000, 252}, - {0xc000, 253}, - }, - /* 209 */ - { - {0x8001, 211}, - {0xc016, 211}, - {0x8001, 212}, - {0xc016, 212}, - {0x8001, 214}, - {0xc016, 214}, - {0x8001, 221}, - {0xc016, 221}, - {0x8001, 222}, - {0xc016, 222}, - {0x8001, 223}, - {0xc016, 223}, - {0x8001, 241}, - {0xc016, 241}, - {0x8001, 244}, - {0xc016, 244}, - }, - /* 210 */ - { - {0x8002, 211}, - {0x8009, 211}, - {0x8017, 211}, - {0xc028, 211}, - {0x8002, 212}, - {0x8009, 212}, - {0x8017, 212}, - {0xc028, 212}, - {0x8002, 214}, - {0x8009, 214}, - {0x8017, 214}, - {0xc028, 214}, - {0x8002, 221}, - {0x8009, 221}, - {0x8017, 221}, - {0xc028, 221}, - }, - /* 211 */ - { - {0x8003, 211}, - {0x8006, 211}, - {0x800a, 211}, - {0x800f, 211}, - {0x8018, 211}, - {0x801f, 211}, - {0x8029, 211}, - {0xc038, 211}, - {0x8003, 212}, - {0x8006, 212}, - {0x800a, 212}, - {0x800f, 212}, - {0x8018, 212}, - {0x801f, 212}, - {0x8029, 212}, - {0xc038, 212}, - }, - /* 212 */ - { - {0x8003, 214}, - {0x8006, 214}, - {0x800a, 214}, - {0x800f, 214}, - {0x8018, 214}, - {0x801f, 214}, - {0x8029, 214}, - {0xc038, 214}, - {0x8003, 221}, - {0x8006, 221}, - {0x800a, 221}, - {0x800f, 221}, - {0x8018, 221}, - {0x801f, 221}, - {0x8029, 221}, - {0xc038, 221}, - }, - /* 213 */ - { - {0x8002, 222}, - {0x8009, 222}, - {0x8017, 222}, - {0xc028, 222}, - {0x8002, 223}, - {0x8009, 223}, - {0x8017, 223}, - {0xc028, 223}, - {0x8002, 241}, - {0x8009, 241}, - {0x8017, 241}, - {0xc028, 241}, - {0x8002, 244}, - {0x8009, 244}, - {0x8017, 244}, - {0xc028, 244}, - }, - /* 214 */ - { - {0x8003, 222}, - {0x8006, 222}, - {0x800a, 222}, - {0x800f, 222}, - {0x8018, 222}, - {0x801f, 222}, - {0x8029, 222}, - {0xc038, 222}, - {0x8003, 223}, - {0x8006, 223}, - {0x800a, 223}, - {0x800f, 223}, - {0x8018, 223}, - {0x801f, 223}, - {0x8029, 223}, - {0xc038, 223}, - }, - /* 215 */ - { - {0x8003, 241}, - {0x8006, 241}, - {0x800a, 241}, - {0x800f, 241}, - {0x8018, 241}, - {0x801f, 241}, - {0x8029, 241}, - {0xc038, 241}, - {0x8003, 244}, - {0x8006, 244}, - {0x800a, 244}, - {0x800f, 244}, - {0x8018, 244}, - {0x801f, 244}, - {0x8029, 244}, - {0xc038, 244}, - }, - /* 216 */ - { - {0x8001, 245}, - {0xc016, 245}, - {0x8001, 246}, - {0xc016, 246}, - {0x8001, 247}, - {0xc016, 247}, - {0x8001, 248}, - {0xc016, 248}, - {0x8001, 250}, - {0xc016, 250}, - {0x8001, 251}, - {0xc016, 251}, - {0x8001, 252}, - {0xc016, 252}, - {0x8001, 253}, - {0xc016, 253}, - }, - /* 217 */ - { - {0x8002, 245}, - {0x8009, 245}, - {0x8017, 245}, - {0xc028, 245}, - {0x8002, 246}, - {0x8009, 246}, - {0x8017, 246}, - {0xc028, 246}, - {0x8002, 247}, - {0x8009, 247}, - {0x8017, 247}, - {0xc028, 247}, - {0x8002, 248}, - {0x8009, 248}, - {0x8017, 248}, - {0xc028, 248}, - }, - /* 218 */ - { - {0x8003, 245}, - {0x8006, 245}, - {0x800a, 245}, - {0x800f, 245}, - {0x8018, 245}, - {0x801f, 245}, - {0x8029, 245}, - {0xc038, 245}, - {0x8003, 246}, - {0x8006, 246}, - {0x800a, 246}, - {0x800f, 246}, - {0x8018, 246}, - {0x801f, 246}, - {0x8029, 246}, - {0xc038, 246}, - }, - /* 219 */ - { - {0x8003, 247}, - {0x8006, 247}, - {0x800a, 247}, - {0x800f, 247}, - {0x8018, 247}, - {0x801f, 247}, - {0x8029, 247}, - {0xc038, 247}, - {0x8003, 248}, - {0x8006, 248}, - {0x800a, 248}, - {0x800f, 248}, - {0x8018, 248}, - {0x801f, 248}, - {0x8029, 248}, - {0xc038, 248}, - }, - /* 220 */ - { - {0x8002, 250}, - {0x8009, 250}, - {0x8017, 250}, - {0xc028, 250}, - {0x8002, 251}, - {0x8009, 251}, - {0x8017, 251}, - {0xc028, 251}, - {0x8002, 252}, - {0x8009, 252}, - {0x8017, 252}, - {0xc028, 252}, - {0x8002, 253}, - {0x8009, 253}, - {0x8017, 253}, - {0xc028, 253}, - }, - /* 221 */ - { - {0x8003, 250}, - {0x8006, 250}, - {0x800a, 250}, - {0x800f, 250}, - {0x8018, 250}, - {0x801f, 250}, - {0x8029, 250}, - {0xc038, 250}, - {0x8003, 251}, - {0x8006, 251}, - {0x800a, 251}, - {0x800f, 251}, - {0x8018, 251}, - {0x801f, 251}, - {0x8029, 251}, - {0xc038, 251}, - }, - /* 222 */ - { - {0x8003, 252}, - {0x8006, 252}, - {0x800a, 252}, - {0x800f, 252}, - {0x8018, 252}, - {0x801f, 252}, - {0x8029, 252}, - {0xc038, 252}, - {0x8003, 253}, - {0x8006, 253}, - {0x800a, 253}, - {0x800f, 253}, - {0x8018, 253}, - {0x801f, 253}, - {0x8029, 253}, - {0xc038, 253}, - }, - /* 223 */ - { - {0xc000, 254}, - {0xe3, 0}, - {0xe5, 0}, - {0xe6, 0}, - {0xe9, 0}, - {0xea, 0}, - {0xec, 0}, - {0xed, 0}, - {0xf1, 0}, - {0xf2, 0}, - {0xf4, 0}, - {0xf5, 0}, - {0xf8, 0}, - {0xf9, 0}, - {0xfb, 0}, - {0xfc, 0}, - }, - /* 224 */ - { - {0x8001, 254}, - {0xc016, 254}, - {0xc000, 2}, - {0xc000, 3}, - {0xc000, 4}, - {0xc000, 5}, - {0xc000, 6}, - {0xc000, 7}, - {0xc000, 8}, - {0xc000, 11}, - {0xc000, 12}, - {0xc000, 14}, - {0xc000, 15}, - {0xc000, 16}, - {0xc000, 17}, - {0xc000, 18}, - }, - /* 225 */ - { - {0x8002, 254}, - {0x8009, 254}, - {0x8017, 254}, - {0xc028, 254}, - {0x8001, 2}, - {0xc016, 2}, - {0x8001, 3}, - {0xc016, 3}, - {0x8001, 4}, - {0xc016, 4}, - {0x8001, 5}, - {0xc016, 5}, - {0x8001, 6}, - {0xc016, 6}, - {0x8001, 7}, - {0xc016, 7}, - }, - /* 226 */ - { - {0x8003, 254}, - {0x8006, 254}, - {0x800a, 254}, - {0x800f, 254}, - {0x8018, 254}, - {0x801f, 254}, - {0x8029, 254}, - {0xc038, 254}, - {0x8002, 2}, - {0x8009, 2}, - {0x8017, 2}, - {0xc028, 2}, - {0x8002, 3}, - {0x8009, 3}, - {0x8017, 3}, - {0xc028, 3}, - }, - /* 227 */ - { - {0x8003, 2}, - {0x8006, 2}, - {0x800a, 2}, - {0x800f, 2}, - {0x8018, 2}, - {0x801f, 2}, - {0x8029, 2}, - {0xc038, 2}, - {0x8003, 3}, - {0x8006, 3}, - {0x800a, 3}, - {0x800f, 3}, - {0x8018, 3}, - {0x801f, 3}, - {0x8029, 3}, - {0xc038, 3}, - }, - /* 228 */ - { - {0x8002, 4}, - {0x8009, 4}, - {0x8017, 4}, - {0xc028, 4}, - {0x8002, 5}, - {0x8009, 5}, - {0x8017, 5}, - {0xc028, 5}, - {0x8002, 6}, - {0x8009, 6}, - {0x8017, 6}, - {0xc028, 6}, - {0x8002, 7}, - {0x8009, 7}, - {0x8017, 7}, - {0xc028, 7}, - }, - /* 229 */ - { - {0x8003, 4}, - {0x8006, 4}, - {0x800a, 4}, - {0x800f, 4}, - {0x8018, 4}, - {0x801f, 4}, - {0x8029, 4}, - {0xc038, 4}, - {0x8003, 5}, - {0x8006, 5}, - {0x800a, 5}, - {0x800f, 5}, - {0x8018, 5}, - {0x801f, 5}, - {0x8029, 5}, - {0xc038, 5}, - }, - /* 230 */ - { - {0x8003, 6}, - {0x8006, 6}, - {0x800a, 6}, - {0x800f, 6}, - {0x8018, 6}, - {0x801f, 6}, - {0x8029, 6}, - {0xc038, 6}, - {0x8003, 7}, - {0x8006, 7}, - {0x800a, 7}, - {0x800f, 7}, - {0x8018, 7}, - {0x801f, 7}, - {0x8029, 7}, - {0xc038, 7}, - }, - /* 231 */ - { - {0x8001, 8}, - {0xc016, 8}, - {0x8001, 11}, - {0xc016, 11}, - {0x8001, 12}, - {0xc016, 12}, - {0x8001, 14}, - {0xc016, 14}, - {0x8001, 15}, - {0xc016, 15}, - {0x8001, 16}, - {0xc016, 16}, - {0x8001, 17}, - {0xc016, 17}, - {0x8001, 18}, - {0xc016, 18}, - }, - /* 232 */ - { - {0x8002, 8}, - {0x8009, 8}, - {0x8017, 8}, - {0xc028, 8}, - {0x8002, 11}, - {0x8009, 11}, - {0x8017, 11}, - {0xc028, 11}, - {0x8002, 12}, - {0x8009, 12}, - {0x8017, 12}, - {0xc028, 12}, - {0x8002, 14}, - {0x8009, 14}, - {0x8017, 14}, - {0xc028, 14}, - }, - /* 233 */ - { - {0x8003, 8}, - {0x8006, 8}, - {0x800a, 8}, - {0x800f, 8}, - {0x8018, 8}, - {0x801f, 8}, - {0x8029, 8}, - {0xc038, 8}, - {0x8003, 11}, - {0x8006, 11}, - {0x800a, 11}, - {0x800f, 11}, - {0x8018, 11}, - {0x801f, 11}, - {0x8029, 11}, - {0xc038, 11}, - }, - /* 234 */ - { - {0x8003, 12}, - {0x8006, 12}, - {0x800a, 12}, - {0x800f, 12}, - {0x8018, 12}, - {0x801f, 12}, - {0x8029, 12}, - {0xc038, 12}, - {0x8003, 14}, - {0x8006, 14}, - {0x800a, 14}, - {0x800f, 14}, - {0x8018, 14}, - {0x801f, 14}, - {0x8029, 14}, - {0xc038, 14}, - }, - /* 235 */ - { - {0x8002, 15}, - {0x8009, 15}, - {0x8017, 15}, - {0xc028, 15}, - {0x8002, 16}, - {0x8009, 16}, - {0x8017, 16}, - {0xc028, 16}, - {0x8002, 17}, - {0x8009, 17}, - {0x8017, 17}, - {0xc028, 17}, - {0x8002, 18}, - {0x8009, 18}, - {0x8017, 18}, - {0xc028, 18}, - }, - /* 236 */ - { - {0x8003, 15}, - {0x8006, 15}, - {0x800a, 15}, - {0x800f, 15}, - {0x8018, 15}, - {0x801f, 15}, - {0x8029, 15}, - {0xc038, 15}, - {0x8003, 16}, - {0x8006, 16}, - {0x800a, 16}, - {0x800f, 16}, - {0x8018, 16}, - {0x801f, 16}, - {0x8029, 16}, - {0xc038, 16}, - }, - /* 237 */ - { - {0x8003, 17}, - {0x8006, 17}, - {0x800a, 17}, - {0x800f, 17}, - {0x8018, 17}, - {0x801f, 17}, - {0x8029, 17}, - {0xc038, 17}, - {0x8003, 18}, - {0x8006, 18}, - {0x800a, 18}, - {0x800f, 18}, - {0x8018, 18}, - {0x801f, 18}, - {0x8029, 18}, - {0xc038, 18}, - }, - /* 238 */ - { - {0xc000, 19}, - {0xc000, 20}, - {0xc000, 21}, - {0xc000, 23}, - {0xc000, 24}, - {0xc000, 25}, - {0xc000, 26}, - {0xc000, 27}, - {0xc000, 28}, - {0xc000, 29}, - {0xc000, 30}, - {0xc000, 31}, - {0xc000, 127}, - {0xc000, 220}, - {0xc000, 249}, - {0xfd, 0}, - }, - /* 239 */ - { - {0x8001, 19}, - {0xc016, 19}, - {0x8001, 20}, - {0xc016, 20}, - {0x8001, 21}, - {0xc016, 21}, - {0x8001, 23}, - {0xc016, 23}, - {0x8001, 24}, - {0xc016, 24}, - {0x8001, 25}, - {0xc016, 25}, - {0x8001, 26}, - {0xc016, 26}, - {0x8001, 27}, - {0xc016, 27}, - }, - /* 240 */ - { - {0x8002, 19}, - {0x8009, 19}, - {0x8017, 19}, - {0xc028, 19}, - {0x8002, 20}, - {0x8009, 20}, - {0x8017, 20}, - {0xc028, 20}, - {0x8002, 21}, - {0x8009, 21}, - {0x8017, 21}, - {0xc028, 21}, - {0x8002, 23}, - {0x8009, 23}, - {0x8017, 23}, - {0xc028, 23}, - }, - /* 241 */ - { - {0x8003, 19}, - {0x8006, 19}, - {0x800a, 19}, - {0x800f, 19}, - {0x8018, 19}, - {0x801f, 19}, - {0x8029, 19}, - {0xc038, 19}, - {0x8003, 20}, - {0x8006, 20}, - {0x800a, 20}, - {0x800f, 20}, - {0x8018, 20}, - {0x801f, 20}, - {0x8029, 20}, - {0xc038, 20}, - }, - /* 242 */ - { - {0x8003, 21}, - {0x8006, 21}, - {0x800a, 21}, - {0x800f, 21}, - {0x8018, 21}, - {0x801f, 21}, - {0x8029, 21}, - {0xc038, 21}, - {0x8003, 23}, - {0x8006, 23}, - {0x800a, 23}, - {0x800f, 23}, - {0x8018, 23}, - {0x801f, 23}, - {0x8029, 23}, - {0xc038, 23}, - }, - /* 243 */ - { - {0x8002, 24}, - {0x8009, 24}, - {0x8017, 24}, - {0xc028, 24}, - {0x8002, 25}, - {0x8009, 25}, - {0x8017, 25}, - {0xc028, 25}, - {0x8002, 26}, - {0x8009, 26}, - {0x8017, 26}, - {0xc028, 26}, - {0x8002, 27}, - {0x8009, 27}, - {0x8017, 27}, - {0xc028, 27}, - }, - /* 244 */ - { - {0x8003, 24}, - {0x8006, 24}, - {0x800a, 24}, - {0x800f, 24}, - {0x8018, 24}, - {0x801f, 24}, - {0x8029, 24}, - {0xc038, 24}, - {0x8003, 25}, - {0x8006, 25}, - {0x800a, 25}, - {0x800f, 25}, - {0x8018, 25}, - {0x801f, 25}, - {0x8029, 25}, - {0xc038, 25}, - }, - /* 245 */ - { - {0x8003, 26}, - {0x8006, 26}, - {0x800a, 26}, - {0x800f, 26}, - {0x8018, 26}, - {0x801f, 26}, - {0x8029, 26}, - {0xc038, 26}, - {0x8003, 27}, - {0x8006, 27}, - {0x800a, 27}, - {0x800f, 27}, - {0x8018, 27}, - {0x801f, 27}, - {0x8029, 27}, - {0xc038, 27}, - }, - /* 246 */ - { - {0x8001, 28}, - {0xc016, 28}, - {0x8001, 29}, - {0xc016, 29}, - {0x8001, 30}, - {0xc016, 30}, - {0x8001, 31}, - {0xc016, 31}, - {0x8001, 127}, - {0xc016, 127}, - {0x8001, 220}, - {0xc016, 220}, - {0x8001, 249}, - {0xc016, 249}, - {0xfe, 0}, - {0xff, 0}, - }, - /* 247 */ - { - {0x8002, 28}, - {0x8009, 28}, - {0x8017, 28}, - {0xc028, 28}, - {0x8002, 29}, - {0x8009, 29}, - {0x8017, 29}, - {0xc028, 29}, - {0x8002, 30}, - {0x8009, 30}, - {0x8017, 30}, - {0xc028, 30}, - {0x8002, 31}, - {0x8009, 31}, - {0x8017, 31}, - {0xc028, 31}, - }, - /* 248 */ - { - {0x8003, 28}, - {0x8006, 28}, - {0x800a, 28}, - {0x800f, 28}, - {0x8018, 28}, - {0x801f, 28}, - {0x8029, 28}, - {0xc038, 28}, - {0x8003, 29}, - {0x8006, 29}, - {0x800a, 29}, - {0x800f, 29}, - {0x8018, 29}, - {0x801f, 29}, - {0x8029, 29}, - {0xc038, 29}, - }, - /* 249 */ - { - {0x8003, 30}, - {0x8006, 30}, - {0x800a, 30}, - {0x800f, 30}, - {0x8018, 30}, - {0x801f, 30}, - {0x8029, 30}, - {0xc038, 30}, - {0x8003, 31}, - {0x8006, 31}, - {0x800a, 31}, - {0x800f, 31}, - {0x8018, 31}, - {0x801f, 31}, - {0x8029, 31}, - {0xc038, 31}, - }, - /* 250 */ - { - {0x8002, 127}, - {0x8009, 127}, - {0x8017, 127}, - {0xc028, 127}, - {0x8002, 220}, - {0x8009, 220}, - {0x8017, 220}, - {0xc028, 220}, - {0x8002, 249}, - {0x8009, 249}, - {0x8017, 249}, - {0xc028, 249}, - {0xc000, 10}, - {0xc000, 13}, - {0xc000, 22}, - {0x100, 0}, - }, - /* 251 */ - { - {0x8003, 127}, - {0x8006, 127}, - {0x800a, 127}, - {0x800f, 127}, - {0x8018, 127}, - {0x801f, 127}, - {0x8029, 127}, - {0xc038, 127}, - {0x8003, 220}, - {0x8006, 220}, - {0x800a, 220}, - {0x800f, 220}, - {0x8018, 220}, - {0x801f, 220}, - {0x8029, 220}, - {0xc038, 220}, - }, - /* 252 */ - { - {0x8003, 249}, - {0x8006, 249}, - {0x800a, 249}, - {0x800f, 249}, - {0x8018, 249}, - {0x801f, 249}, - {0x8029, 249}, - {0xc038, 249}, - {0x8001, 10}, - {0xc016, 10}, - {0x8001, 13}, - {0xc016, 13}, - {0x8001, 22}, - {0xc016, 22}, - {0x100, 0}, - {0x100, 0}, - }, - /* 253 */ - { - {0x8002, 10}, - {0x8009, 10}, - {0x8017, 10}, - {0xc028, 10}, - {0x8002, 13}, - {0x8009, 13}, - {0x8017, 13}, - {0xc028, 13}, - {0x8002, 22}, - {0x8009, 22}, - {0x8017, 22}, - {0xc028, 22}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, - /* 254 */ - { - {0x8003, 10}, - {0x8006, 10}, - {0x800a, 10}, - {0x800f, 10}, - {0x8018, 10}, - {0x801f, 10}, - {0x8029, 10}, - {0xc038, 10}, - {0x8003, 13}, - {0x8006, 13}, - {0x800a, 13}, - {0x800f, 13}, - {0x8018, 13}, - {0x801f, 13}, - {0x8029, 13}, - {0xc038, 13}, - }, - /* 255 */ - { - {0x8003, 22}, - {0x8006, 22}, - {0x800a, 22}, - {0x800f, 22}, - {0x8018, 22}, - {0x801f, 22}, - {0x8029, 22}, - {0xc038, 22}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, - /* 256 */ - { - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, + /* 0 */ + { + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, + }, + /* 1 */ + { + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, + }, + /* 2 */ + { + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, + }, + /* 3 */ + { + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, + }, + /* 4 */ + { + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, + }, + /* 5 */ + { + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, + }, + /* 6 */ + { + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, + }, + /* 7 */ + { + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, + }, + /* 8 */ + { + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, + }, + /* 9 */ + { + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, + }, + /* 10 */ + { + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, + }, + /* 11 */ + { + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, + }, + /* 12 */ + { + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, + }, + /* 13 */ + { + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, + }, + /* 14 */ + { + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, + }, + /* 15 */ + { + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, + }, + /* 16 */ + { + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, + }, + /* 17 */ + { + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, + }, + /* 18 */ + { + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, + }, + /* 19 */ + { + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, + }, + /* 20 */ + { + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, + }, + /* 21 */ + { + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, + }, + /* 22 */ + { + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, + }, + /* 23 */ + { + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, + }, + /* 24 */ + { + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, + }, + /* 25 */ + { + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, + }, + /* 26 */ + { + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, + }, + /* 27 */ + { + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, + }, + /* 28 */ + { + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, + }, + /* 29 */ + { + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, + }, + /* 30 */ + { + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, + }, + /* 31 */ + { + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, + }, + /* 32 */ + { + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, + }, + /* 33 */ + { + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, + }, + /* 34 */ + { + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, + }, + /* 35 */ + { + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, + }, + /* 36 */ + { + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, + }, + /* 37 */ + { + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, + }, + /* 38 */ + { + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, + }, + /* 39 */ + { + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, + }, + /* 40 */ + { + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, + }, + /* 41 */ + { + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, + }, + /* 42 */ + { + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, + }, + /* 43 */ + { + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, + }, + /* 44 */ + { + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, + }, + /* 45 */ + { + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, + }, + /* 46 */ + { + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, + }, + /* 47 */ + { + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, + }, + /* 48 */ + { + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, + }, + /* 49 */ + { + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, + }, + /* 50 */ + { + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, + }, + /* 51 */ + { + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, + }, + /* 52 */ + { + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, + }, + /* 53 */ + { + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, + }, + /* 54 */ + { + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, + }, + /* 55 */ + { + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, + }, + /* 56 */ + { + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, + }, + /* 57 */ + { + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, + }, + /* 58 */ + { + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, + }, + /* 59 */ + { + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, + }, + /* 60 */ + { + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, + }, + /* 61 */ + { + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, + }, + /* 62 */ + { + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, + }, + /* 63 */ + { + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, + }, + /* 64 */ + { + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, + }, + /* 65 */ + { + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, + }, + /* 66 */ + { + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, + }, + /* 67 */ + { + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, + }, + /* 68 */ + { + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, + }, + /* 69 */ + { + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, + }, + /* 70 */ + { + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, + }, + /* 71 */ + { + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, + }, + /* 72 */ + { + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, + }, + /* 73 */ + { + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, + }, + /* 74 */ + { + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, + }, + /* 75 */ + { + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, + }, + /* 76 */ + { + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, + }, + /* 77 */ + { + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, + }, + /* 78 */ + { + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, + }, + /* 79 */ + { + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, + }, + /* 80 */ + { + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, + }, + /* 81 */ + { + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, + }, + /* 82 */ + { + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, + }, + /* 83 */ + { + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, + }, + /* 84 */ + { + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, + }, + /* 85 */ + { + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, + }, + /* 86 */ + { + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, + }, + /* 87 */ + { + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, + }, + /* 88 */ + { + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, + }, + /* 89 */ + { + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, + }, + /* 90 */ + { + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, + }, + /* 91 */ + { + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, + }, + /* 92 */ + { + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, + }, + /* 93 */ + { + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, + }, + /* 94 */ + { + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, + }, + /* 95 */ + { + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, + }, + /* 96 */ + { + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, + }, + /* 97 */ + { + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, + }, + /* 98 */ + { + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, + }, + /* 99 */ + { + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, + }, + /* 100 */ + { + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, + }, + /* 101 */ + { + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, + }, + /* 102 */ + { + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, + }, + /* 103 */ + { + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, + }, + /* 104 */ + { + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, + }, + /* 105 */ + { + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, + }, + /* 106 */ + { + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, + }, + /* 107 */ + { + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, + }, + /* 108 */ + { + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, + }, + /* 109 */ + { + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, + }, + /* 110 */ + { + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, + }, + /* 111 */ + { + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, + }, + /* 112 */ + { + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, + }, + /* 113 */ + { + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, + }, + /* 114 */ + { + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, + }, + /* 115 */ + { + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, + }, + /* 116 */ + { + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, + }, + /* 117 */ + { + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, + }, + /* 118 */ + { + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, + }, + /* 119 */ + { + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, + }, + /* 120 */ + { + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, + }, + /* 121 */ + { + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, + }, + /* 122 */ + { + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, + }, + /* 123 */ + { + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, + }, + /* 124 */ + { + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, + }, + /* 125 */ + { + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, + }, + /* 126 */ + { + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, + }, + /* 127 */ + { + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, + }, + /* 128 */ + { + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, + }, + /* 129 */ + { + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, + }, + /* 130 */ + { + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, + }, + /* 131 */ + { + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, + }, + /* 132 */ + { + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, + }, + /* 133 */ + { + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, + }, + /* 134 */ + { + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, + }, + /* 135 */ + { + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, + }, + /* 136 */ + { + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, + }, + /* 137 */ + { + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, + }, + /* 138 */ + { + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, + }, + /* 139 */ + { + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, + }, + /* 140 */ + { + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, + }, + /* 141 */ + { + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, + }, + /* 142 */ + { + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, + }, + /* 143 */ + { + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, + }, + /* 144 */ + { + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, + }, + /* 145 */ + { + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, + }, + /* 146 */ + { + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, + }, + /* 147 */ + { + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, + }, + /* 148 */ + { + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, + }, + /* 149 */ + { + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, + }, + /* 150 */ + { + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, + }, + /* 151 */ + { + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, + }, + /* 152 */ + { + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, + }, + /* 153 */ + { + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, + }, + /* 154 */ + { + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, + }, + /* 155 */ + { + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, + }, + /* 156 */ + { + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, + }, + /* 157 */ + { + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, + }, + /* 158 */ + { + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, + }, + /* 159 */ + { + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, + }, + /* 160 */ + { + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, + }, + /* 161 */ + { + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, + }, + /* 162 */ + { + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, + }, + /* 163 */ + { + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, + }, + /* 164 */ + { + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, + }, + /* 165 */ + { + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, + }, + /* 166 */ + { + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, + }, + /* 167 */ + { + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, + }, + /* 168 */ + { + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, + }, + /* 169 */ + { + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, + }, + /* 170 */ + { + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, + }, + /* 171 */ + { + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, + }, + /* 172 */ + { + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, + }, + /* 173 */ + { + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, + }, + /* 174 */ + { + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, + }, + /* 175 */ + { + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, + }, + /* 176 */ + { + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, + }, + /* 177 */ + { + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, + }, + /* 178 */ + { + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, + }, + /* 179 */ + { + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, + }, + /* 180 */ + { + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, + }, + /* 181 */ + { + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, + }, + /* 182 */ + { + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, + }, + /* 183 */ + { + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, + }, + /* 184 */ + { + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, + }, + /* 185 */ + { + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, + }, + /* 186 */ + { + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, + }, + /* 187 */ + { + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, + }, + /* 188 */ + { + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, + }, + /* 189 */ + { + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, + }, + /* 190 */ + { + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, + }, + /* 191 */ + { + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, + }, + /* 192 */ + { + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, + }, + /* 193 */ + { + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, + }, + /* 194 */ + { + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, + }, + /* 195 */ + { + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, + }, + /* 196 */ + { + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, + }, + /* 197 */ + { + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, + }, + /* 198 */ + { + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, + }, + /* 199 */ + { + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, + }, + /* 200 */ + { + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, + }, + /* 201 */ + { + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, + }, + /* 202 */ + { + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, + }, + /* 203 */ + { + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, + }, + /* 204 */ + { + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, + }, + /* 205 */ + { + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, + }, + /* 206 */ + { + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, + }, + /* 207 */ + { + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, + }, + /* 208 */ + { + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, + }, + /* 209 */ + { + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, + }, + /* 210 */ + { + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, + }, + /* 211 */ + { + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, + }, + /* 212 */ + { + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, + }, + /* 213 */ + { + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, + }, + /* 214 */ + { + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, + }, + /* 215 */ + { + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, + }, + /* 216 */ + { + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, + }, + /* 217 */ + { + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, + }, + /* 218 */ + { + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, + }, + /* 219 */ + { + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, + }, + /* 220 */ + { + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, + }, + /* 221 */ + { + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, + }, + /* 222 */ + { + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, + }, + /* 223 */ + { + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, + }, + /* 224 */ + { + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, + }, + /* 225 */ + { + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, + }, + /* 226 */ + { + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, + }, + /* 227 */ + { + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, + }, + /* 228 */ + { + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, + }, + /* 229 */ + { + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, + }, + /* 230 */ + { + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, + }, + /* 231 */ + { + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, + }, + /* 232 */ + { + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, + }, + /* 233 */ + { + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, + }, + /* 234 */ + { + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, + }, + /* 235 */ + { + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, + }, + /* 236 */ + { + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, + }, + /* 237 */ + { + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, + }, + /* 238 */ + { + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, + }, + /* 239 */ + { + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, + }, + /* 240 */ + { + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, + }, + /* 241 */ + { + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, + }, + /* 242 */ + { + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, + }, + /* 243 */ + { + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, + }, + /* 244 */ + { + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, + }, + /* 245 */ + { + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, + }, + /* 246 */ + { + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, + }, + /* 247 */ + { + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, + }, + /* 248 */ + { + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, + }, + /* 249 */ + { + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, + }, + /* 250 */ + { + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, + }, + /* 251 */ + { + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, + }, + /* 252 */ + { + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, + }, + /* 253 */ + { + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 254 */ + { + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, + }, + /* 255 */ + { + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, }; diff --git a/thirdparty/nghttp2/nghttp2_helper.c b/thirdparty/nghttp2/nghttp2_helper.c index 765e5492ce5..8ee736af877 100644 --- a/thirdparty/nghttp2/nghttp2_helper.c +++ b/thirdparty/nghttp2/nghttp2_helper.c @@ -117,6 +117,8 @@ const char *nghttp2_strerror(int error_code) { "closed"; case NGHTTP2_ERR_TOO_MANY_SETTINGS: return "SETTINGS frame contained more than the maximum allowed entries"; + case NGHTTP2_ERR_TOO_MANY_CONTINUATIONS: + return "Too many CONTINUATION frames following a HEADER frame"; default: return "Unknown error code"; } diff --git a/thirdparty/nghttp2/nghttp2_mem.c b/thirdparty/nghttp2/nghttp2_mem.c index 8ca2ed332fd..6a449cffd70 100644 --- a/thirdparty/nghttp2/nghttp2_mem.c +++ b/thirdparty/nghttp2/nghttp2_mem.c @@ -25,6 +25,8 @@ #include "nghttp2_mem.h" static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + return malloc(size); } diff --git a/thirdparty/nghttp2/nghttp2ver.h b/thirdparty/nghttp2/nghttp2ver.h new file mode 100644 index 00000000000..827c9989684 --- /dev/null +++ b/thirdparty/nghttp2/nghttp2ver.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "1.64.0" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +#define NGHTTP2_VERSION_NUM 0x014000 + +#endif /* NGHTTP2VER_H */ diff --git a/thirdparty/nlohmann/LICENSE.MIT b/thirdparty/nlohmann/LICENSE.MIT new file mode 100644 index 00000000000..f0622d6dc24 --- /dev/null +++ b/thirdparty/nlohmann/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2021 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/nlohmann/adl_serializer.hpp b/thirdparty/nlohmann/adl_serializer.hpp new file mode 100644 index 00000000000..eeaa1425740 --- /dev/null +++ b/thirdparty/nlohmann/adl_serializer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/from_json.hpp b/thirdparty/nlohmann/detail/conversions/from_json.hpp new file mode 100644 index 00000000000..c389dca7ad3 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/from_json.hpp @@ -0,0 +1,389 @@ +#pragma once + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value and + not std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.begin(), j.end(), std::begin(l)); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template ::value and + not is_constructible_object_type::value and + not is_constructible_string_type::value and + not is_basic_json::value, + int > = 0 > + +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +template ::value>> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template ::value>> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_chars.hpp b/thirdparty/nlohmann/detail/conversions/to_chars.hpp new file mode 100644 index 00000000000..d99703a54c1 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_chars.hpp @@ -0,0 +1,1106 @@ +#pragma once + +#include // array +#include // assert +#include // or, and, not +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional +#include + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + assert(x.e == y.e); + assert(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + assert(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + assert(std::isfinite(value)); + assert(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 and E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + assert(kAlpha <= cached.e + e + 64); + assert(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + and delta - rest >= ten_k + and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + { + assert(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + assert(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + assert(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + assert(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + assert(e > -1000); + assert(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n and n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n - k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (n + 2); + } + + if (0 < n and n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + assert(k > n); + + std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + buf[n] = '.'; + return buf + (k + 1); + } + + if (min_exp < n and n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2 + (-n) + k); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + buf[1] = '.'; + buf += 1 + k; + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + assert(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last - first >= kMaxExp + 2); + assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + assert(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_json.hpp b/thirdparty/nlohmann/detail/conversions/to_json.hpp new file mode 100644 index 00000000000..a1def699fd7 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_json.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include // copy +#include // or, and, not +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template ::value and + not is_compatible_object_type< + BasicJsonType, CompatibleArrayType>::value and + not is_compatible_string_type::value and + not is_basic_json::value, + int> = 0> +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template::value and not is_basic_json::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t::value, + int> = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template < typename BasicJsonType, typename T, + enable_if_t>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/exceptions.hpp b/thirdparty/nlohmann/detail/exceptions.hpp new file mode 100644 index 00000000000..ed836188ccc --- /dev/null +++ b/thirdparty/nlohmann/detail/exceptions.hpp @@ -0,0 +1,356 @@ +#pragma once + +#include // exception +#include // runtime_error +#include // to_string + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.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. +json.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. +json.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. +json.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. +json.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. +json.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`. +json.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. +json.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. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.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. +json.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. +json.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. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.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. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.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(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.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. +json.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. +json.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 &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.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. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.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. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.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. +json.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. | +json.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) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.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. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.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. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.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 | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/https/github.com/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/binary_reader.hpp b/thirdparty/nlohmann/detail/input/binary_reader.hpp new file mode 100644 index 00000000000..1b6e0f9b7b3 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/binary_reader.hpp @@ -0,0 +1,1983 @@ +#pragma once + +#include // generate_n +#include // array +#include // assert +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move + +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using json_sax_t = SAX; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + assert(ia); + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result and strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } + + return result; + } + + /*! + @brief determine system byte order + + @return true if and only if system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static constexpr bool little_endianess(int num = 1) noexcept + { + return *reinterpret_cast(&num) == 1; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + + return true; + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + 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"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const int element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number; + return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len; + string_t value; + return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + 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()))); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + while (int element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) + { + return false; + } + + if (not is_array and not sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) and sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu)); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1)); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu)); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1)); + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const int byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + assert(0 <= exp and exp <= 32); + assert(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (not get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) and sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xCA: // float 32 + { + float number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + 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"))); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'I': + { + std::int16_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + 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"))); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const int prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'I': + { + std::int16_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'l': + { + std::int32_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'L': + { + std::int64_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'd': + { + float number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + 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"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) and sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + 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"))); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return current = ia->get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + int get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string"))) + { + success = false; + } + return static_cast(current); + }); + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/input_adapters.hpp b/thirdparty/nlohmann/detail/input/input_adapters.hpp new file mode 100644 index 00000000000..9512a771eef --- /dev/null +++ b/thirdparty/nlohmann/detail/input/input_adapters.hpp @@ -0,0 +1,442 @@ +#pragma once + +#include // array +#include // assert +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +/*! +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of +exactly one non-EOF character for future input. The int_type characters +returned consist of all valid char values as positive values (typically +unsigned char), plus an EOF value outside that range, specified by the value +of the function std::char_traits::eof(). This value is typically -1, but +could be any arbitrary value which is not a valid char value. +*/ +struct input_adapter_protocol +{ + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + virtual ~input_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; + +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter : public input_adapter_protocol +{ + public: + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = default; + ~file_input_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter : public input_adapter_protocol +{ + public: + ~input_stream_adapter() override + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + is.clear(is.rdstate() & std::ios::eofbit); + } + + explicit input_stream_adapter(std::istream& i) + : is(i), sb(*i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() override + { + auto res = sb.sbumpc(); + // set eof manually, as we don't use the istream interface. + if (res == EOF) + { + is.clear(is.rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream& is; + std::streambuf& sb; +}; + +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol +{ + public: + input_buffer_adapter(const char* b, const std::size_t l) noexcept + : cursor(b), limit(b == nullptr ? nullptr : (b + l)) + {} + + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + ~input_buffer_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + if (JSON_HEDLEY_LIKELY(cursor < limit)) + { + assert(cursor != nullptr and limit != nullptr); + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + private: + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* const limit; +}; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc or wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (current_wchar < str.size()) + { + const auto wc2 = static_cast(str[current_wchar++]); + const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + ++current_wchar; + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +template +class wide_string_input_adapter : public input_adapter_protocol +{ + public: + explicit wide_string_input_adapter(const WideStringType& w) noexcept + : str(w) + {} + + std::char_traits::int_type get_character() noexcept override + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index == 0); + } + + // use buffer + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// the wstring to process + const WideStringType& str; + + /// index of the current wchar in str + std::size_t current_wchar = 0; + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + +class input_adapter +{ + public: + // native support + JSON_HEDLEY_NON_NULL(2) + input_adapter(std::FILE* file) + : ia(std::make_shared(file)) {} + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + input_adapter(const std::wstring& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u16string& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u32string& ws) + : ia(std::make_shared>(ws)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) + { +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; + } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/json_sax.hpp b/thirdparty/nlohmann/detail/input/json_sax.hpp new file mode 100644 index 00000000000..606b7862ebd --- /dev/null +++ b/thirdparty/nlohmann/detail/input/json_sax.hpp @@ -0,0 +1,701 @@ +#pragma once + +#include // assert +#include +#include // string +#include // move +#include // vector + +#include +#include + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + /// type for (signed) integers + using number_integer_t = typename BasicJsonType::number_integer_t; + /// type for unsigned integers + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + /// type for floating-point numbers + using number_float_t = typename BasicJsonType::number_float_t; + /// type for strings + using string_t = typename BasicJsonType::string_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + assert(ref_stack.back()->is_object()); + assert(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep and ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() and not callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (not keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + assert(not keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (not keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback or callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (not keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (not ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + assert(ref_stack.back()->is_object()); + // check if we should store an element for the current key + assert(not key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (not store_element) + { + return {false, nullptr}; + } + + assert(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/lexer.hpp b/thirdparty/nlohmann/detail/input/lexer.hpp new file mode 100644 index 00000000000..0843d749d6a --- /dev/null +++ b/thirdparty/nlohmann/detail/input/lexer.hpp @@ -0,0 +1,1512 @@ +#pragma once + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } + + explicit lexer(detail::input_adapter_t&& adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = delete; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = delete; + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' and current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + assert(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char* literal_text, const std::size_t length, + token_type return_type) + { + assert(current == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + std::char_traits::int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia->get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + assert(not token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(int c) + { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if ('\x00' <= c and c <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(c); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB and get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 and not skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + detail::input_adapter_t ia = nullptr; + + /// the current character + std::char_traits::int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/parser.hpp b/thirdparty/nlohmann/detail/input/parser.hpp new file mode 100644 index 00000000000..8d4febcbfab --- /dev/null +++ b/thirdparty/nlohmann/detail/input/parser.hpp @@ -0,0 +1,498 @@ +#pragma once + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +/*! +@brief syntax analysis + +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + using parser_callback_t = + std::function; + + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result and strict and (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (not skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(not sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (not context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/position_t.hpp b/thirdparty/nlohmann/detail/input/position_t.hpp new file mode 100644 index 00000000000..14e9649fb29 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/position_t.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp new file mode 100644 index 00000000000..2c81f723fd2 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iter_impl.hpp b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp new file mode 100644 index 00000000000..3a362971909 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp @@ -0,0 +1,638 @@ +#pragma once + +#include // not +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + assert(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp new file mode 100644 index 00000000000..c61d96296ce --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + target = std::to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std diff --git a/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp new file mode 100644 index 00000000000..4cced80caf4 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include // random_access_iterator_tag + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp new file mode 100644 index 00000000000..f3b5b5db6b8 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp new file mode 100644 index 00000000000..28d6f1a65df --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_pointer.hpp b/thirdparty/nlohmann/detail/json_pointer.hpp new file mode 100644 index 00000000000..87af3423315 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_pointer.hpp @@ -0,0 +1,1011 @@ +#pragma once + +#include // all_of +#include // assert +#include // isdigit +#include // accumulate +#include // string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_index array index to append + @return JSON pointer with @a array_index appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_index) + { + return *this /= std::to_string(array_index); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_index array index + @return a new JSON pointer with @a array_index appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) + { + return json_pointer(ptr) /= array_index; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) + { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; + } + + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + JSON_TRY + { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // use unchecked array access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (not ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + JSON_TRY + { + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(not value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return not (lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_ref.hpp b/thirdparty/nlohmann/detail/json_ref.hpp new file mode 100644 index 00000000000..c8dec7330f2 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_ref.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)), is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), + is_rvalue(true) {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/macro_scope.hpp b/thirdparty/nlohmann/detail/macro_scope.hpp new file mode 100644 index 00000000000..27dddc6be76 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_scope.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include // pair +#include + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json diff --git a/thirdparty/nlohmann/detail/macro_unscope.hpp b/thirdparty/nlohmann/detail/macro_unscope.hpp new file mode 100644 index 00000000000..80b293e7d59 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_unscope.hpp @@ -0,0 +1,21 @@ +#pragma once + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL + +#include diff --git a/thirdparty/nlohmann/detail/meta/cpp_future.hpp b/thirdparty/nlohmann/detail/meta/cpp_future.hpp new file mode 100644 index 00000000000..948cd4fb0cb --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/cpp_future.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/meta/detected.hpp b/thirdparty/nlohmann/detail/meta/detected.hpp new file mode 100644 index 00000000000..5b52460acf6 --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/detected.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template