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 75b88982311..b9232561366 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,104 +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 - - 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) - - build-ubuntu-old: - runs-on: ubuntu-16.04 - steps: - - uses: actions/checkout@v1 - - name: install-deps - run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl - - 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) + - 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 - - 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: 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 00d551a63b1..00000000000 --- a/.github/workflows/lib.yml +++ /dev/null @@ -1,51 +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 667baaf60c3..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.7.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) @@ -139,13 +147,13 @@ target_link_libraries(co swoole) # ext-swoole file(GLOB ext_cxx_files ext-src/*.cc) set(ext_src_list ${ext_cxx_files} + thirdparty/php/curl/interface.cc + thirdparty/php/curl/multi.cc thirdparty/php/sockets/multicast.cc thirdparty/php/sockets/sendrecvmsg.cc 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 @@ -159,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 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/CREDITS b/CREDITS deleted file mode 100644 index 7f55d94500b..00000000000 --- a/CREDITS +++ /dev/null @@ -1,3 +0,0 @@ -Tianfeng Han(mikan.tenny@gmail.com) -Swoole Inc. -QQ: 350749960 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 43bbf6f53a4..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,537 +0,0 @@ -[English](./README.md) | 中文 - -# Swoole - -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases) -[![License](https://img.shields.io/badge/license-apache2-blue.svg)](LICENSE) -[![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) - -![](./mascot.png) - -**Swoole是一个C++编写的基于异步事件驱动和协程的并行网络通信引擎,为PHP提供高性能网络编程支持** - -## ✨事件驱动 - -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 386c9a7a1f2..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,142 @@ -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 + +Run Swoole program by [Docker](https://github.com/swoole/docker-swoole) + +```bash +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). + +## 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.** + +### 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 +## ✨ Event-based The network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests. Swoole 4.x uses a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance. -## ⚡️Coroutine +## ⚡ Coroutine Swoole 4.x or later supports the built-in coroutine with high availability, and you can use fully synchronized code to implement asynchronous performance. PHP code without any additional keywords, the underlying automatic coroutine-scheduling. @@ -437,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) @@ -448,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 ``` @@ -473,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 @@ -501,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 928a746cdea..b1e237f77de 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,155 +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/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 \ @@ -647,21 +1113,63 @@ 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" + if test -z "$PHP_CONFIG"; then + AC_MSG_ERROR([php-config not found]) + fi + + SW_PHP_VERSION=`$PHP_CONFIG --version` + 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" @@ -671,10 +1179,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" ] @@ -694,7 +1205,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 @@ -718,19 +1229,37 @@ if test "$PHP_SWOOLE" != "no"; then else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "mips64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="mips64_n64_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi elif test "$SW_CPU" = "mips32"; then if test "$SW_OS" = "LINUX"; then SW_CONTEXT_ASM_FILE="mips32_o32_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "riscv64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="riscv64_sysv_elf_gas.S" + 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 @@ -740,12 +1269,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 @@ -756,7 +1287,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() @@ -768,6 +1303,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) @@ -781,9 +1320,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/httplib_server.h b/core-tests/include/httplib_server.h index 4f807ad275b..489893f2837 100644 --- a/core-tests/include/httplib_server.h +++ b/core-tests/include/httplib_server.h @@ -758,7 +758,7 @@ inline bool Server::listen_internal() { } else if (svr_sock_->errCode == ECANCELED) { break; } else { - swWarn("accept failed, Error: %s[%d]", svr_sock_->errMsg, svr_sock_->errCode); + swoole_warning("accept failed, Error: %s[%d]", svr_sock_->errMsg, svr_sock_->errCode); break; } } diff --git a/core-tests/include/test_core.h b/core-tests/include/test_core.h index 2c64130ae0b..21feb2da8b6 100644 --- a/core-tests/include/test_core.h +++ b/core-tests/include/test_core.h @@ -14,21 +14,51 @@ #include #include #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 e3d7fe318f9..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,48 +23,44 @@ 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::vector ports; std::unordered_map private_data; std::string host; int port; int mode; int type; -public: + public: DgramPacket *packet = nullptr; Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type); ~Server(); void on(std::string event, void *fn); bool start(); - bool listen(std::string host, int port, enum swSocket_type type); + bool listen(std::string host, int port, enum swSocketType type); int send(int session_id, const void *data, uint32_t length); 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 094afb32165..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 | +----------------------------------------------------------------------+ */ @@ -36,13 +36,13 @@ Server::Server(std::string _host, int _port, swoole::Server::Mode _mode, int _ty serv.dispatch_mode = 2; serv.private_data_2 = this; - if (!listen(host, port, (swSocket_type) type)) { - swWarn("listen(%s:%d) fail[error=%d].", host.c_str(), port, errno); + if (!listen(host, port, (swSocketType) type)) { + swoole_warning("listen(%s:%d) fail[error=%d].", host.c_str(), port, errno); exit(0); } if (serv.create() < 0) { - swWarn("create server fail[error=%d].", errno); + swoole_warning("create server fail[error=%d].", errno); exit(0); } } @@ -75,8 +75,8 @@ bool Server::start() { return serv.start() == 0; } -bool Server::listen(std::string host, int port, enum swSocket_type type) { - swListenPort *ls = serv.add_port(type, (char *) host.c_str(), port); +bool Server::listen(std::string host, int port, enum swSocketType type) { + ListenPort *ls = serv.add_port(type, (char *) host.c_str(), port); if (ls == nullptr) { return false; } diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index 4f0771e2114..671a0aa7235 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -13,13 +13,16 @@ | @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_server.h" #include "swoole_file.h" #include "swoole_util.h" +#include "swoole.h" +#include "swoole_config.h" using namespace swoole; using namespace std; @@ -31,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); } @@ -41,7 +45,7 @@ TEST(base, dec2hex) { sw_free(result); } -TEST(base, swoole_hex2dec) { +TEST(base, hex2dec) { size_t n_parsed; ASSERT_EQ(swoole_hex2dec("9fff9123", &n_parsed), 2684326179); ASSERT_EQ(n_parsed, 8); @@ -72,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) { @@ -109,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) { @@ -122,17 +143,18 @@ TEST(base, file_size) { } TEST(base, eventdata_pack) { - swEventData ed1 { }; + EventData ed1{}; - ASSERT_TRUE(ed1.pack(test_data.c_str(), test_data.length())); + ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length())); ASSERT_EQ(string(ed1.data, ed1.info.len), test_data); - swEventData ed2 { }; + EventData ed2{}; ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG); - ASSERT_TRUE(ed2.pack(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); + ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); String _buffer(SW_BUFFER_SIZE_BIG); - ASSERT_TRUE(ed2.unpack(&_buffer)); + PacketPtr packet; + ASSERT_TRUE(Server::task_unpack(&ed2, &_buffer, &packet)); ASSERT_EQ(memcmp(sw_tg_buffer()->str, _buffer.str, SW_BUFFER_SIZE_BIG), 0); } @@ -164,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(); @@ -186,27 +216,23 @@ 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); } @@ -214,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)); } @@ -229,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 7cb06bd6e4e..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 | +----------------------------------------------------------------------+ */ @@ -36,14 +36,14 @@ TEST(channel, push) { char buf[8000]; int n = swoole_random_bytes(buf, (rand() % (sizeof(buf) / 2)) + (sizeof(buf) / 2)); if (n <= 0) { - swTrace("no enough data, n=%d, errno=%d\n", n, errno); + swoole_trace("no enough data, n=%d, errno=%d\n", n, errno); continue; } m[index++] = string(buf, n); bytes += n; } - swTrace("size=%lu", m.size()); + swoole_trace("size=%lu", m.size()); thread t1([&]() { auto next = m.find(0); @@ -52,7 +52,7 @@ TEST(channel, push) { while (bytes < N) { if (c->push(next->second.c_str(), next->second.length()) == SW_OK) { - swTrace("[PUSH] index=%d, size=%lu", index, next->second.length()); + swoole_trace("[PUSH] index=%d, size=%lu", index, next->second.length()); bytes += next->second.length(); next = m.find(index++); if (next == m.end()) { @@ -71,7 +71,7 @@ TEST(channel, push) { while (bytes < N) { int retval = c->pop(buf, sizeof(buf)); if (retval > 0) { - swTrace("[POP] index=%d, size=%d", index, retval); + swoole_trace("[POP] index=%d, size=%d", index, retval); string &_data = m[index++]; bytes += retval; ASSERT_EQ(_data, string(buf, retval)); @@ -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 9d96c1acf5d..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); - ASSERT_FALSE(content->contains(SW_STRL("hello info"))); - ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); - ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + 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"))); + + 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); @@ -130,15 +150,30 @@ void TestPrettyName::fun(bool strip, const char *expect_str) { ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); } -void test_pretty_name(bool strip, const char *expect_str) { +static void test_pretty_name(bool strip, const char *expect_str) { ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); } + +static void test_pretty_name_lambda(bool strip, const char *expect_str) { + auto fn = [](bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); + }; + fn(strip, expect_str); +} + } // namespace TestA -void test_pretty_name(bool strip, const char *expect_str) { +static void test_pretty_name(bool strip, const char *expect_str) { ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); } +static void test_pretty_name_lambda(bool strip, const char *expect_str) { + auto fn = [](bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); + }; + fn(strip, expect_str); +} + TEST(log, pretty_name) { TestA::TestPrettyName::fun(false, "TestA::TestPrettyName::fun"); TestA::test_pretty_name(false, "TestA::test_pretty_name"); @@ -148,3 +183,33 @@ TEST(log, pretty_name) { TestA::test_pretty_name(true, "test_pretty_name"); test_pretty_name(true, "test_pretty_name"); } + +TEST(log, pretty_name_lambda) { + TestA::test_pretty_name_lambda(true, "test_pretty_name_lambda"); + test_pretty_name_lambda(true, "test_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 9932df92d20..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(); }); - 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 24e9b426797..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 ) { - swWarn("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/ringbuffer.cpp b/core-tests/src/memory/ringbuffer.cpp index 5a2ba192107..00f7d327f21 100644 --- a/core-tests/src/memory/ringbuffer.cpp +++ b/core-tests/src/memory/ringbuffer.cpp @@ -75,7 +75,7 @@ static void thread_write(void) { } while (yield_count < 100); if (!ptr) { - swWarn("alloc failed. index=%d, break", i); + swoole_warning("alloc failed. index=%d, break", i); } ASSERT_NE(ptr, nullptr); 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 3d7ede93fec..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 | +----------------------------------------------------------------------+ */ @@ -21,6 +21,8 @@ #include "swoole_socket.h" +#include "swoole_util.h" + using namespace swoole; using swoole::coroutine::Socket; using swoole::coroutine::System; @@ -61,7 +63,7 @@ TEST(dns, cancel) { // swoole_set_log_level(SW_LOG_TRACE); 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(); }); @@ -72,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); @@ -92,8 +87,126 @@ 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); } + +TEST(dns, gethosts) { + char hosts_file[] = "/tmp/swoole_hosts"; + ofstream file(hosts_file); + if (!file.is_open()) { + std::cout << std::string("file open failed: ") + std::string(strerror(errno)) << std::endl; + throw strerror(errno); + } + + ON_SCOPE_EXIT { + unlink(hosts_file); + }; + + file << "\n"; + file << "127.0.0.1\n"; + file << "127.0.0.1 localhost\n"; + file << "# 127.0.0.1 aaa.com\n"; + file << " 127.0.0.1 bbb.com ccc.com #ddd.com\n"; + file.close(); + + swoole_set_hosts_path(hosts_file); + + std::string ip = swoole::coroutine::get_ip_by_hosts("localhost"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("aaa.com"); + ASSERT_EQ(ip, ""); + + ip = swoole::coroutine::get_ip_by_hosts("bbb.com"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("ccc.com"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("ddd.com"); + ASSERT_EQ(ip, ""); + + 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 6f7b16e0bec..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 | +----------------------------------------------------------------------+ */ @@ -53,7 +53,7 @@ TEST(socket, sendto) { unlink(sock2_path); } -static void test_sendto(enum swSocket_type sock_type) { +static void test_sendto(enum swSocketType sock_type) { int port1 = 0, port2 = 0; const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; @@ -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 2416bfe3e87..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,17 +21,19 @@ #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) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -47,7 +49,7 @@ TEST(stream, send) { ASSERT_EQ(serv.create(), SW_OK); std::thread t1([&]() { - swSignal_none(); + swoole_signal_block_all(); lock.lock(); @@ -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 dffcf9011b3..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 | +----------------------------------------------------------------------+ */ @@ -24,7 +24,7 @@ #include -using swoole::AsyncEvent; +using namespace swoole; static int callback_count; @@ -48,3 +48,52 @@ TEST(async, dispatch) { ASSERT_EQ(handle_count, 1000); ASSERT_EQ(callback_count, 1000); } + +TEST(async, schedule) { + callback_count = 0; + std::atomic handle_count(0); + + int N = 1000; + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + AsyncEvent event{}; + event.object = &handle_count; + event.callback = [](AsyncEvent *event) { callback_count++; }; + event.handler = [](AsyncEvent *event) { + usleep(swoole_rand(50000, 100000)); + (*(std::atomic *) event->object)++; + }; + + SwooleG.aio_core_worker_num = 4; + SwooleG.aio_worker_num = 128; + SwooleG.aio_max_wait_time = 0.05; + SwooleG.aio_max_idle_time = 0.5; + + int count = N; + swoole_timer_tick(2, [&count, &event, N](Timer *, TimerNode *timer) { + SW_LOOP_N(swoole_rand(5, 15)) { + auto ret = swoole::async::dispatch(&event); + EXPECT_EQ(ret->object, event.object); + count--; + if (count == 0) { + swoole_timer_del(timer); + 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); + } + } + }); + + swoole_event_wait(); + + ASSERT_EQ(handle_count, N); + ASSERT_EQ(callback_count, N); +} 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/pipe.cpp b/core-tests/src/os/pipe.cpp index 5238acf7493..f966e0cb2ef 100644 --- a/core-tests/src/os/pipe.cpp +++ b/core-tests/src/os/pipe.cpp @@ -19,7 +19,7 @@ TEST(pipe, unixsock) { // 1 ret = p.read(buf, sizeof(buf)); if (ret < 0) { - swSysWarn("read() failed."); + swoole_sys_warning("read() failed."); } ASSERT_GT(ret, 0); ASSERT_EQ(strcmp("hello world1", buf), 0); diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 75d5cbb29bb..5f043f7a431 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -7,102 +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); +} + +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) { +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_SOCKET), SW_OK); - ASSERT_EQ(pool.listen(TEST_TMP_FILE, 128), SW_OK); + 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); @@ -118,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 a636d446898..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); @@ -17,17 +15,16 @@ TEST(os_signal, swSignalfd_set) { ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 0); - swSignalfd_init(); - swSignal_set(SIGUSR1, sig_usr1); - swSignalfd_setup(SwooleTG.reactor); + swoole_signalfd_init(); + swoole_signal_set(SIGUSR1, sig_usr1); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 1); - swSignal_set(SIGUSR1, NULL); - swSignal_clear(); + swoole_signal_set(SIGUSR1, NULL); + swoole_signal_clear(); 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 720e136ec99..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,18 +66,18 @@ 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 = swRedis_parse(req->data, req->info.len); + auto list = redis::parse(req->data, req->info.len); String *buffer = sw_tg_buffer(); buffer->clear(); if (strcasecmp(list[0].c_str(), "GET") == 0) { - swRedis_format(buffer, SW_REDIS_REPLY_STRING, REDIS_TEST_VALUE); + redis::format(buffer, redis::REPLY_STRING, REDIS_TEST_VALUE); serv->send(session_id, buffer->str, buffer->length); } else if (strcasecmp(list[0].c_str(), "SET") == 0) { - swRedis_format(buffer, SW_REDIS_REPLY_STATUS, "OK"); + redis::format(buffer, redis::REPLY_STATUS, "OK"); serv->send(session_id, buffer->str, buffer->length); } diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index 5147703adfb..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) { @@ -38,20 +40,20 @@ TEST(reactor, create) { /** * coroutine socket reactor */ - ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CORO_SOCKET | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CORO_SOCKET | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CORO_SOCKET | SW_EVENT_ERROR)], nullptr); + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_ERROR)], nullptr); /** * system reactor */ - ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CORO_POLL | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CORO_POLL | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CORO_POLL | SW_EVENT_ERROR)], nullptr); + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_ERROR)], nullptr); - ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CORO_EVENT | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CORO_EVENT | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CORO_EVENT | SW_EVENT_ERROR)], nullptr); + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_ERROR)], nullptr); ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_AIO | SW_EVENT_READ)], nullptr); @@ -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")); @@ -106,12 +109,14 @@ TEST(reactor, write) { int ret; UnixSocket p(true, SOCK_DGRAM); ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); ASSERT_EQ(ret, SW_OK); ASSERT_NE(SwooleTG.reactor, nullptr); - swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, swEvent *ev) -> int { + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { char buffer[16]; ssize_t n = read(ev->fd, buffer, sizeof(buffer)); @@ -125,21 +130,82 @@ TEST(reactor, write) { ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); ASSERT_EQ(ret, SW_OK); - ret = swoole_event_write(p.get_socket(true), (void *) SW_STRS("hello world")); - ASSERT_EQ(ret, sizeof("hello world")); + auto sock = p.get_socket(true); + + auto n = swoole_event_write(sock, (void *) SW_STRS("hello world")); + ASSERT_EQ(n, sizeof("hello world")); ret = swoole_event_wait(); ASSERT_EQ(ret, SW_OK); ASSERT_EQ(SwooleTG.reactor, nullptr); } +constexpr int DATA_SIZE = 2 * SW_NUM_MILLION; + +TEST(reactor, write_2m) { + int ret; + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + auto tg_buf = sw_tg_buffer(); + ssize_t n = read(ev->fd, tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length); + if (n <= 0) { + return SW_ERR; + } + tg_buf->grow(n); + if (tg_buf->length == DATA_SIZE) { + tg_buf->append('\0'); + reactor->del(ev->socket); + } + return SW_OK; + }); + + p.set_blocking(false); + p.set_buffer_size(65536); + + ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(ret, SW_OK); + + String str(DATA_SIZE); + str.append_random_bytes(str.size - 1, false); + str.append('\0'); + + sw_tg_buffer()->clear(); + + auto n = swoole_event_write(p.get_socket(true), str.value(), str.get_length()); + ASSERT_EQ(n, str.get_length()); + ASSERT_GT(p.get_socket(true)->out_buffer->length(), 1024); + + ret = swoole_event_wait(); + ASSERT_EQ(ret, SW_OK); + ASSERT_FALSE(swoole_event_is_available()); + ASSERT_STREQ(sw_tg_buffer()->value(), str.value()); +} + +TEST(reactor, bad_fd) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + auto sock = make_socket(999999, SW_FD_STREAM_CLIENT); + sock->nonblock = 1; + auto n = swoole_event_write(sock, SW_STRL("hello world")); + ASSERT_EQ(n, SW_ERR); + ASSERT_EQ(swoole_get_last_error(), EBADF); + swoole_event_free(); + sock->move_fd(); + sock->free(); +} + static const char *pkt = "hello world\r\n"; static void reactor_test_func(Reactor *reactor) { Pipe p(true); ASSERT_TRUE(p.ready()); - reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, swEvent *event) -> int { + reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *event) -> int { char buf[1024]; size_t l = strlen(pkt); size_t n = read(event->fd, buf, sizeof(buf)); @@ -150,7 +216,7 @@ static void reactor_test_func(Reactor *reactor) { return SW_OK; }); - reactor->set_handler(SW_FD_PIPE | SW_EVENT_WRITE, [](Reactor *reactor, swEvent *event) -> int { + reactor->set_handler(SW_FD_PIPE | SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int { size_t l = strlen(pkt); EXPECT_EQ(write(event->fd, pkt, l), l); reactor->del(event->socket); @@ -194,3 +260,116 @@ TEST(reactor, add_or_update) { swoole_event_free(); } + +TEST(reactor, defer_task) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Reactor *reactor = sw_reactor(); + ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS); + + int count = 0; + reactor->defer([&count](void *) { count++; }); + swoole_event_wait(); + 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 0098cd9fafc..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,14 +26,14 @@ 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) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -43,7 +43,7 @@ TEST(server, send_buffer) { lock.lock(); std::thread t1([&]() { - swSignal_none(); + swoole_signal_block_all(); lock.lock(); @@ -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 b98af28aca8..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; @@ -41,15 +49,15 @@ struct http_context { response_headers[key] = value; } - void response(enum swHttp_status_code code, string body) { + void response(enum swHttpStatusCode code, string body) { response_headers["Content-Length"] = to_string(body.length()); response(code); server->send(fd, body.c_str(), body.length()); } void response(int code) { - swString *buf = swoole::make_string(1024); - buf->length = sw_snprintf(buf->str, buf->size, "HTTP/1.1 %s\r\n", swHttp_get_status_message(code)); + 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()); buf->append(SW_STRL(": ")); @@ -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,9 +108,9 @@ 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) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } port->open_http_protocol = 1; @@ -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,24 +201,236 @@ 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) { - swSignal_none(); + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + kill(getpid(), SIGTERM); + }); +} + +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) { - swSignal_none(); + test_base_server([](Server *serv) { + swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -207,8 +447,8 @@ TEST(http_server, post) { } TEST(http_server, static_get) { - test_run_server([](swServer *serv) { - swSignal_none(); + 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,18 +601,18 @@ static void websocket_test(int server_port, const char *data, size_t length) { } TEST(http_server, websocket_small) { - test_run_server([](swServer *serv) { - swSignal_none(); + 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); }); } TEST(http_server, websocket_medium) { - test_run_server([](swServer *serv) { - swSignal_none(); + 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) { - swSignal_none(); + 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 new file mode 100644 index 00000000000..52f96e78a4f --- /dev/null +++ b/core-tests/src/server/message_bus.cpp @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | 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_server.h" +#include "swoole_memory.h" +#include "swoole_signal.h" +#include "swoole_lock.h" + +using namespace std; +using namespace swoole; + +constexpr int DATA_SIZE = 2 * SW_NUM_MILLION; + +struct TestPacket { + SessionId fd; + std::string data; +}; + +struct TestMB { + std::vector q; + MessageBus mb; + std::function read_func; + + bool send_empty_packet(network::Socket *sock) { + SendData _data4; + _data4.data = "hello world"; + _data4.info.fd = 4; + _data4.info.len = 0; + if (!mb.write(sock, &_data4)) { + return false; + } + + SendData _data5; + _data5.data = nullptr; + _data5.info.fd = 5; + _data5.info.len = 10; + if (!mb.write(sock, &_data5)) { + return false; + } + + return true; + } + + int read(Event *ev) { + auto retval = read_func(ev->socket); + if (retval == 0) { + return SW_OK; + } else if (retval < 0) { + swoole_event_del(ev->socket); + return SW_ERR; + } + + auto packet = mb.get_packet(); + + q.push_back(TestPacket{ + mb.get_buffer()->info.fd, + std::string(packet.data, packet.length), + }); + + if (q.size() == 5) { + swoole_event_del(ev->socket); + } + + return SW_OK; + } +}; + +#define MB_SEND(i, s) \ + String pkt##i(s); \ + pkt##i.append_random_bytes(pkt##i.size - 1, false); \ + pkt##i.append('\0'); \ + \ + SendData _data##i{}; \ + _data##i.data = pkt##i.value(); \ + _data##i.info.fd = i; \ + _data##i.info.len = pkt##i.get_length(); \ + ASSERT_TRUE(tmb.mb.write(p.get_socket(true), &_data##i)); + +#define MB_ASSERT(i) \ + auto r##i = tmb.q.at(i - 1); \ + ASSERT_EQ(r##i.fd, i); \ + ASSERT_STREQ(r##i.data.c_str(), pkt##i.value()); + +TEST(message_bus, read) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} + +TEST(message_bus, read_with_buffer) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read_with_buffer(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} 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 8487ecd8cbc..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,50 +23,40 @@ #include "swoole_memory.h" #include "swoole_signal.h" #include "swoole_lock.h" +#include "swoole_util.h" using namespace std; using namespace swoole; -TEST(server, create_pipe_buffers) { - int ret; - Server serv(Server::MODE_PROCESS); - serv.create(); - - ret = serv.create_pipe_buffers(); - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, serv.pipe_buffers); - for (uint32_t i = 0; i < serv.reactor_num; i++) { - ASSERT_NE(nullptr, serv.pipe_buffers[i]); - } -} +int beforeReloadPid = 0; TEST(server, schedule) { int ret; Server serv(Server::MODE_PROCESS); serv.worker_num = 6; - serv.dispatch_mode = SW_DISPATCH_QUEUE; + serv.dispatch_mode = Server::DISPATCH_IDLE_WORKER; ret = serv.create(); 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; for (uint32_t i = 0; i < serv.worker_num; i++) { - auto worker_id = serv.schedule_worker(i*13, nullptr); + auto worker_id = serv.schedule_worker(i * 13, nullptr); _worker_id_set.insert(worker_id); } 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(); for (uint32_t i = 0; i < serv.worker_num; i++) { - auto worker_id = serv.schedule_worker(i*13, nullptr); + auto worker_id = serv.schedule_worker(i * 13, nullptr); _worker_id_set.insert(worker_id); } ASSERT_EQ(_worker_id_set.size(), serv.worker_num - 2); @@ -80,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; @@ -89,11 +79,11 @@ TEST(server, base) { ASSERT_EQ(serv.create(), SW_OK); std::thread t1([&]() { - swSignal_none(); + swoole_signal_block_all(); 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]; @@ -103,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); @@ -131,21 +121,21 @@ TEST(server, process) { ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } ASSERT_EQ(serv.create(), SW_OK); - serv.onStart = [&lock](swServer *serv) { + serv.onStart = [&lock](Server *serv) { thread t1([=]() { - swSignal_none(); + 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]; @@ -157,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); @@ -173,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); @@ -185,9 +374,9 @@ TEST(server, ssl) { Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); lock->lock(); - ListenPort *port = serv.add_port((enum swSocket_type )(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0); + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -199,16 +388,16 @@ TEST(server, ssl) { serv.onStart = [&lock](Server *serv) { thread t1([=]() { - swSignal_none(); + swoole_signal_block_all(); lock->lock(); ListenPort *port = serv->get_primary_port(); EXPECT_EQ(port->ssl, 1); - EXPECT_EQ(swSSL_is_thread_safety(), true); + 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)); @@ -221,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)); @@ -248,9 +437,9 @@ TEST(server, dtls) { Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); lock->lock(); - ListenPort *port = serv.add_port((enum swSocket_type )(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -262,7 +451,7 @@ TEST(server, dtls) { serv.onStart = [&lock](Server *serv) { thread t1([=]() { - swSignal_none(); + swoole_signal_block_all(); lock->lock(); @@ -270,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)); @@ -283,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)); @@ -298,22 +487,70 @@ 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) { - swServer serv; + Server serv; serv.worker_num = 1; serv.task_worker_num = 1; - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); 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; }; @@ -323,23 +560,262 @@ 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); - swEventData buf; + EventData buf; memset(&buf.info, 0, sizeof(buf.info)); - swTask_type(&buf) |= SW_TASK_NOREPLY; + buf.info.ext_flags = SW_TASK_NOREPLY; buf.info.len = strlen(packet); memcpy(buf.data, packet, strlen(packet)); 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) { @@ -418,3 +894,336 @@ TEST(server, reactor_num_zero) { ASSERT_EQ(serv.reactor_num, SW_CPU_NUM); } + +void test_command(enum Server::Mode _mode) { + Server serv(_mode); + serv.worker_num = 4; + serv.task_worker_num = 4; + serv.reactor_num = 2; + + SwooleG.running = 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); + } + + ASSERT_EQ(serv.create(), SW_OK); + + serv.add_command("test", Server::Command::ALL_PROCESS, [](Server *, const std::string &msg) -> std::string { + return std::string("json result, ") + msg; + }); + + serv.onStart = [](Server *serv) { + static Server::Command::Callback fn = [&](Server *serv, const std::string &msg) { + usleep(50000); + if (msg == "json result, hello world [0]") { + if (serv->is_base_mode()) { + goto _send_to_event_worker; + } else { + serv->command(1, Server::Command::REACTOR_THREAD, "test", "hello world [1]", fn); + } + } else if (msg == "json result, hello world [1]") { + _send_to_event_worker: + serv->command(1, Server::Command::EVENT_WORKER, "test", "hello world [2]", fn); + } else if (msg == "json result, hello world [2]") { + serv->command(1, Server::Command::TASK_WORKER, "test", "hello world [3]", fn); + } else if (msg == "json result, hello world [3]") { + serv->command(1, Server::Command::MANAGER, "test", "hello world [4]", fn); + } else if (msg == "json result, hello world [4]") { + swoole_timer_after(50, [serv](Timer *, TimerNode *) { serv->shutdown(); }); + } else { + ASSERT_TRUE(0); + } + }; + serv->command(1, Server::Command::MASTER, "test", "hello world [0]", fn); + }; + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + + }; + + serv.onTask = [](Server *, EventData *) -> int { return SW_OK; }; + + 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; + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, command_1) { + test_command(Server::MODE_PROCESS); +} + +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/docs/swoole-logo.svg b/docs/swoole-logo.svg new file mode 100644 index 00000000000..6297cc67437 --- /dev/null +++ b/docs/swoole-logo.svg @@ -0,0 +1,15 @@ + + + + Transfon SWOOLE - Registered + + + + \ No newline at end of file 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/join.php b/examples/coroutine/join.php new file mode 100644 index 00000000000..4431e7e6af0 --- /dev/null +++ b/examples/coroutine/join.php @@ -0,0 +1,36 @@ + 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 1f6249fb79d..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) { - swNotice("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) { @@ -42,7 +42,7 @@ int main(int argc, char **argv) { ListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -52,12 +52,12 @@ int main(int argc, char **argv) { memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); if (serv.create()) { - swWarn("create server fail[error=%d]", swoole_get_last_error()); + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); exit(1); } if (serv.start() < 0) { - swWarn("start server fail[error=%d]", swoole_get_last_error()); + swoole_warning("start server fail[error=%d]", swoole_get_last_error()); exit(3); } } diff --git a/examples/cpp/test_server.cc b/examples/cpp/test_server.cc index 80ab845c697..32e44d2c826 100644 --- a/examples/cpp/test_server.cc +++ b/examples/cpp/test_server.cc @@ -8,14 +8,14 @@ using namespace swoole; -int my_onPacket(swServer *serv, swRecvData *req); -int my_onReceive(swServer *serv, swRecvData *req); -void my_onStart(swServer *serv); -void my_onShutdown(swServer *serv); -void my_onConnect(swServer *serv, swDataHead *info); -void my_onClose(swServer *serv, swDataHead *info); -void my_onWorkerStart(swServer *serv, int worker_id); -void my_onWorkerStop(swServer *serv, int worker_id); +int my_onPacket(Server *serv, RecvData *req); +int my_onReceive(Server *serv, RecvData *req); +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, Worker *worker); +void my_onWorkerStop(Server *serv, Worker *worker); static int g_receive_count = 0; @@ -62,7 +62,7 @@ int main(int argc, char **argv) { swListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); if (!port) { - swWarn("listen failed, [error=%d]", swoole_get_last_error()); + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } @@ -72,26 +72,26 @@ int main(int argc, char **argv) { memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); if (serv.create()) { - swWarn("create server fail[error=%d]", swoole_get_last_error()); + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); exit(1); } if (serv.start() < 0) { - swWarn("start server fail[error=%d]", swoole_get_last_error()); + swoole_warning("start server fail[error=%d]", swoole_get_last_error()); exit(3); } return 0; } -void my_onWorkerStart(swServer *serv, int worker_id) { - swNotice("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(swServer *serv, int worker_id) { - swNotice("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(swServer *serv, swRecvData *req) { +int my_onReceive(Server *serv, RecvData *req) { char req_data[SW_IPC_BUFFER_SIZE]; char resp_data[SW_IPC_BUFFER_SIZE]; @@ -101,7 +101,7 @@ int my_onReceive(swServer *serv, swRecvData *req) { memcpy(req_data, req->data, req->info.len); swoole::rtrim(req_data, req->info.len); - swNotice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", + swoole_notice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", g_receive_count, conn->info.get_ip(), conn->info.get_port(), @@ -111,14 +111,14 @@ int my_onReceive(swServer *serv, swRecvData *req) { int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", req->info.len, req_data); if (!serv->send(req->info.fd, resp_data, n)) { - swNotice("send to client fail. errno=%d", errno); + swoole_notice("send to client fail. errno=%d", errno); } else { - swNotice("send %d bytes to client success. data=%s", n, resp_data); + swoole_notice("send %d bytes to client success. data=%s", n, resp_data); } return SW_OK; } -int my_onPacket(swServer *serv, swRecvData *req) { +int my_onPacket(Server *serv, RecvData *req) { char address[256]; int port = 0; int ret = 0; @@ -142,7 +142,7 @@ int my_onPacket(swServer *serv, swRecvData *req) { char *data = packet->data; uint32_t length = packet->length; - swNotice("Packet[client=%s:%d, %d bytes]: data=%.*s", address, port, length, length, data); + swoole_notice("Packet[client=%s:%d, %d bytes]: data=%.*s", address, port, length, length, data); char resp_data[SW_IPC_BUFFER_SIZE]; int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s", length, data); @@ -150,26 +150,26 @@ int my_onPacket(swServer *serv, swRecvData *req) { ret = serv_socket->sendto(address, port, resp_data, n); if (ret < 0) { - swNotice("send to client fail. errno=%d", errno); + swoole_notice("send to client fail. errno=%d", errno); } else { - swNotice("send %d bytes to client success. data=%s", n, resp_data); + swoole_notice("send %d bytes to client success. data=%s", n, resp_data); } return SW_OK; } -void my_onStart(swServer *serv) { - swNotice("Server is running"); +void my_onStart(Server *serv) { + swoole_notice("Server is running"); } -void my_onShutdown(swServer *serv) { - swNotice("Server is shutdown"); +void my_onShutdown(Server *serv) { + swoole_notice("Server is shutdown"); } -void my_onConnect(swServer *serv, swDataHead *info) { - swNotice("PID=%d\tConnect fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); +void my_onConnect(Server *serv, DataHead *info) { + swoole_notice("PID=%d\tConnect fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); } -void my_onClose(swServer *serv, swDataHead *info) { - swNotice("PID=%d\tClose fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); +void my_onClose(Server *serv, DataHead *info) { + swoole_notice("PID=%d\tClose fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); } 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 51544878716..cdb1fa25729 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,26 +1,44 @@ on('connect', function ($serv, $fd, $reactor_id){ -// echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; -//}); -$serv->set(array( - 'worker_num' => 1, +function getpid() +{ + global $serv; + return $serv->mode === SWOOLE_THREAD ? \Swoole\Thread::getId() : posix_getpid(); +} -)); +$serv->set([ + 'worker_num' => 2, + 'task_worker_num' => 3, +]); -$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) - { +$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->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"; + if ($serv->send($fd, "hello {$data}\n") == false) { echo "error\n"; } +}); +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Close.\n"; }); -//$serv->on('close', function ($serv, $fd, $reactor_id) { -// echo "[#".posix_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/array.php b/examples/table/array.php deleted file mode 100644 index 59fb1397616..00000000000 --- a/examples/table/array.php +++ /dev/null @@ -1,18 +0,0 @@ -column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); -$table->create(); - -$table['apple'] = array('id' => 145, 'name' => 'iPhone', 'num' => 3.1415); -$table['google'] = array('id' => 358, 'name' => "AlphaGo", 'num' => 3.1415); - -$table['microsoft']['name'] = "Windows"; -$table['microsoft']['num'] = '1997.03'; - -var_dump($table['apple']); -var_dump($table['microsoft']); - -$table['google']['num'] = 500.90; -var_dump($table['google']); 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 acb2bfdc7da..4f56336441a 100644 --- a/examples/table/server.php +++ b/examples/table/server.php @@ -1,12 +1,12 @@ 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->set(['dispatch_mode' => 1]); +$serv = new Swoole\Server('127.0.0.1', 9501); +$serv->set(['dispatch_mode' => 2]); $serv->table = $table; $serv->on('connect', function($serv, $fd, $reactor_id){ 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 17b0d942655..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 new file mode 100644 index 00000000000..9ca36c7a859 --- /dev/null +++ b/examples/table/usage.php @@ -0,0 +1,13 @@ +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)); +$table->set('b', array('id' => 2, 'name' => "swoole-uk", 'num' => 3.1415)); +$table->set('hello@swoole.co.uk', array('id' => 3, 'name' => 'swoole', 'num' => 3.1415)); + +var_dump($table->get('a')); +var_dump($table->get('b', 'name')); 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 54ef17cee85..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,12 +52,19 @@ END_EXTERN_C() #include #include #endif +#ifdef SW_HAVE_ZSTD +#include +#endif #ifdef SW_USE_CARES #include #endif +using swoole::Server; using swoole::network::Socket; +#ifdef SW_USE_IOURING +using swoole::Iouring; +#endif ZEND_DECLARE_MODULE_GLOBALS(swoole) @@ -69,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); @@ -79,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 @@ -231,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 }; @@ -257,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 @@ -268,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 */ @@ -290,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) { @@ -306,18 +243,11 @@ void php_swoole_register_shutdown_function(const char *function) { zend_fcall_info_init( &function_name, 0, &shutdown_function_entry.fci, &shutdown_function_entry.fci_cache, NULL, NULL); register_user_shutdown_function(Z_STRVAL(function_name), Z_STRLEN(function_name), &shutdown_function_entry); - zval_ptr_dtor(&function_name); #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,11 +314,31 @@ 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); } + // [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)); + } } void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data) { @@ -395,8 +349,59 @@ 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, ...) { @@ -406,13 +411,18 @@ 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() { + SwooleG.bug_report_message += swoole::std_string::format("PHP_VERSION : %s\n", PHP_VERSION); } /* {{{ PHP_MINIT_FUNCTION @@ -444,25 +454,9 @@ 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)); - /** - * mode type - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_BASE", swoole::Server::MODE_BASE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_PROCESS", swoole::Server::MODE_PROCESS); - - /** - * task ipc mode - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_UNSOCK", SW_TASK_IPC_UNIXSOCK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_MSGQUEUE", SW_TASK_IPC_MSGQUEUE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_PREEMPTIVE", SW_TASK_IPC_PREEMPTIVE); - /** * socket type */ @@ -582,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); @@ -592,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); @@ -614,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); @@ -621,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); @@ -636,10 +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); @@ -651,6 +659,11 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_SEND_IN_MASTER", SW_ERROR_SERVER_SEND_IN_MASTER); 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); @@ -672,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 @@ -706,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); /** @@ -734,28 +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(); - 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()); + // init bug report message + bug_report_message_init(); + + 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); @@ -770,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 @@ -811,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; } @@ -851,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 @@ -879,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 @@ -905,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 @@ -923,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(); @@ -999,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"); @@ -1015,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 @@ -1023,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; @@ -1040,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 @@ -1062,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; } @@ -1110,11 +1254,12 @@ static PHP_FUNCTION(swoole_hashcode) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); switch (type) { + case 0: + RETURN_LONG(zend_hash_func(data, l_data)); case 1: RETURN_LONG(hashkit_one_at_a_time(data, l_data)); - break; /* ide */ default: - RETURN_LONG(zend_hash_func(data, l_data)); + RETURN_FALSE; } } @@ -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 8bb1513ec29..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 @@ -44,26 +50,16 @@ struct Function; namespace swoole { -struct PHPContext; - -struct FutureTask { - zval coro_params; - zval *current_coro_return_value_ptr; - void *private_data; - TimerNode *timer; - PHPContext *current_task; -}; - struct PHPContext { + typedef std::function SwapCallback; + JMP_BUF *bailout; zval *vm_stack_top; zval *vm_stack_end; 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; @@ -78,7 +74,21 @@ 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; + SwapCallback *on_close; long pcid; zend_object *context; int64_t last_msec; @@ -90,6 +100,7 @@ class PHPCoroutine { zend_fcall_info_cache *fci_cache; zval *argv; uint32_t argc; + zval *callable; }; struct Config { @@ -99,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, @@ -118,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 @@ -126,29 +141,24 @@ 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(); - // TODO: remove old coro APIs (Manual) - static void yield_m(zval *return_value, FutureTask *task); - static void resume_m(FutureTask *task, zval *retval); - static inline long get_cid() { return sw_likely(activated) ? Coroutine::get_current_cid() : -1; } 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) { @@ -156,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() { @@ -181,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; @@ -217,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 5f159b50632..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,28 +24,51 @@ 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 defer_callback = false; bool timer_callback = false; std::set active_handles; }; @@ -58,28 +81,22 @@ class Multi { int running_handles_ = 0; int last_sockfd; int event_count_ = 0; + bool defer_callback = false; std::unique_ptr selector; CURLcode read_info(); - Socket *create_socket(CURL *cp, curl_socket_t sockfd); + HandleSocket *create_socket(Handle *handle, curl_socket_t sockfd); - Handle *get_handle(CURL *cp) { - Handle *handle = nullptr; - curl_easy_getinfo(cp, CURLINFO_PRIVATE, &handle); - return handle; - } - - 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()) { swoole_timer_del(timer); } timeout_ms_ = timeout_ms; - timer = swoole_timer_add( - timeout_ms, false, [this](Timer *timer, TimerNode *tnode) { + timer = swoole_timer_add(timeout_ms, false, [this](Timer *timer, TimerNode *tnode) { this->timer = nullptr; callback(nullptr, 0); }); @@ -110,6 +127,7 @@ class Multi { } ~Multi() { + del_timer(); curl_multi_cleanup(multi_handle_); } @@ -125,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_); @@ -138,15 +156,15 @@ class Multi { Coroutine *check_bound_co() { if (co) { - swFatalError(SW_ERROR_CO_HAS_BEEN_BOUND, "cURL is executing, cannot be operated"); + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, "cURL is executing, cannot be operated"); return nullptr; } 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); @@ -154,6 +172,6 @@ class Multi { static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp); static int handle_timeout(CURLM *multi, long timeout_ms, void *userp); }; -}; +}; // namespace curl } // namespace swoole #endif 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 37a20b188a3..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_from_fd(int fd, enum swSocket_type type); +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 swSocket_type type); +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 0ff0946fda6..bf378da0323 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -12,17 +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" +#include "swoole_http2.h" #include "thirdparty/swoole_http_parser.h" #include "thirdparty/multipart_parser.h" #include +#include #ifdef SW_HAVE_ZLIB #include @@ -32,36 +34,43 @@ #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 { - HTTP_HEADER_SERVER = 1u << 1, - HTTP_HEADER_CONNECTION = 1u << 2, - HTTP_HEADER_CONTENT_LENGTH = 1u << 3, - HTTP_HEADER_DATE = 1u << 4, - HTTP_HEADER_CONTENT_TYPE = 1u << 5, +#include + +enum swHttpHeaderFlag { + HTTP_HEADER_SERVER = 1u << 1, + HTTP_HEADER_CONNECTION = 1u << 2, + HTTP_HEADER_CONTENT_LENGTH = 1u << 3, + HTTP_HEADER_DATE = 1u << 4, + HTTP_HEADER_CONTENT_TYPE = 1u << 5, HTTP_HEADER_TRANSFER_ENCODING = 1u << 6, - HTTP_HEADER_ACCEPT_ENCODING = 1u << 7, + 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 { @@ -74,11 +83,10 @@ struct Request { uint8_t post_form_urlencoded; zval zdata; + const char *body_at; size_t body_length; - swString *chunked_body; -#ifdef SW_USE_HTTP2 - swString *h2_data_buffer; -#endif + String *chunked_body; + String *h2_data_buffer; // Notice: Do not change the order zval *zobject; @@ -124,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; @@ -139,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; @@ -158,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: @@ -208,15 +273,16 @@ class Stream { // uint8_t priority; // useless now uint32_t id; // flow control - uint32_t send_window; - uint32_t recv_window; + uint32_t remote_window_size; + uint32_t local_window_size; Coroutine *waiting_coroutine = nullptr; 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); @@ -230,17 +296,19 @@ class Session { nghttp2_hd_inflater *inflater = nullptr; nghttp2_hd_deflater *deflater = nullptr; - uint32_t header_table_size; - uint32_t send_window; - uint32_t recv_window; - uint32_t max_concurrent_streams; - uint32_t max_frame_size; + 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; + bool shutting_down; + bool is_coro; http::Context *default_ctx = nullptr; - void *private_data = nullptr; + void *private_data = nullptr; void (*handle)(Session *, Stream *) = nullptr; @@ -248,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) { @@ -272,7 +352,7 @@ static sw_inline zval *swoole_http_init_and_read_property( // Notice: swoole http server properties can not be unset anymore, so we can read it without checking zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(zobject), name, name_len, 0, &rv); array_init(property); - *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); + *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); **zproperty_store_pp = *property; } return *zproperty_store_pp; @@ -296,16 +376,13 @@ static inline bool swoole_http_has_crlf(const char *value, size_t length) { } 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); @@ -316,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); }, @@ -356,21 +431,21 @@ class HeaderSet { const uint8_t flags = NGHTTP2_NV_FLAG_NONE) { if (sw_likely(index < size || nvs[index].name == nullptr)) { nghttp2_nv *nv = &nvs[index]; - name = zend_str_tolower_dup(name, name_len); // auto to lower - nv->name = (uchar *) name; - nv->namelen = name_len; - nv->value = (uchar *) emalloc(value_len); + name = zend_str_tolower_dup(name, name_len); // auto to lower + nv->name = (uchar *) name; + nv->namelen = name_len; + nv->value = (uchar *) emalloc(value_len); memcpy(nv->value, value, value_len); nv->valuelen = value_len; - nv->flags = flags | NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE; - swTraceLog(SW_TRACE_HTTP2, - "name=(%zu)[%.*s], value=(%zu)[%.*s]", - name_len, - (int) name_len, - name, - value_len, - (int) value_len, - value); + nv->flags = flags | NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE; + swoole_trace_log(SW_TRACE_HTTP2, + "name=(%zu)[" SW_ECHO_LEN_BLUE "], value=(%zu)[" SW_ECHO_LEN_CYAN "]", + name_len, + (int) name_len, + name, + value_len, + (int) value_len, + value); } else { php_swoole_fatal_error( E_WARNING, "unexpect http2 header [%.*s] (duplicated or overflow)", (int) name_len, name); @@ -403,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 0e9b8620711..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_onFrame(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 de28f50b76b..41f1d441f2a 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: 05b8161677541803bf1644f9a57057863c67937b */ +/* $Id: f0118343cf7d61224924b4899d173b2877f14c91 */ + +#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" @@ -20,7 +82,8 @@ static const char* swoole_library_source_constants = "\n" "!defined('CURLOPT_HEADEROPT') && define('CURLOPT_HEADEROPT', 229);\n" "!defined('CURLOPT_PROXYHEADER') && define('CURLOPT_PROXYHEADER', 10228);\n" - "!defined('CURLOPT_RESOLVE') && define('CURLOPT_RESOLVE', 10203);\n"; + "!defined('CURLOPT_RESOLVE') && define('CURLOPT_RESOLVE', 10203);\n" + "!defined('CURLOPT_UNIX_SOCKET_PATH') && define('CURLOPT_UNIX_SOCKET_PATH', 10231);\n"; static const char* swoole_library_source_std_exec = "\n" @@ -34,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" @@ -59,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" @@ -85,6 +150,8 @@ static const char* swoole_library_source_core_constant = " /* {{{ EVENT */\n" " public const EVENT_START = 'start';\n" "\n" + " public const EVENT_BEFORE_SHUTDOWN = 'beforeShutdown';\n" + "\n" " public const EVENT_SHUTDOWN = 'shutdown';\n" "\n" " public const EVENT_WORKER_START = 'workerStart';\n" @@ -125,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" @@ -170,6 +239,18 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\n" "\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" " public const OPTION_AIO_WORKER_NUM = 'aio_worker_num';\n" @@ -178,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" @@ -220,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" @@ -268,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" @@ -292,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" @@ -312,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" @@ -320,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" @@ -366,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" @@ -420,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" @@ -440,6 +536,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\n" "\n" + " public const OPTION_BOOTSTRAP = 'bootstrap';\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" @@ -484,8 +584,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" @@ -496,7 +594,31 @@ 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" + "\n" + " public const OPTION_DEADLOCK_CHECK_LIMIT = 'deadlock_check_limit';\n" + "\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 = @@ -513,19 +635,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" @@ -533,41 +649,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" @@ -578,28 +692,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" - " * @return static\n" - " */\n" - " public function trim(): self\n" + " public function trim(string $characters = ''): static\n" " {\n" - " return new static(trim($this->string));\n" + " if ($characters) {\n" + " return new static(trim($this->string, $characters)); // @phpstan-ignore new.static\n" + " }\n" + " return new static(trim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -607,7 +715,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" @@ -615,7 +723,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" @@ -623,38 +731,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" @@ -675,7 +775,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" @@ -692,16 +792,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" @@ -736,57 +846,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"; @@ -822,14 +921,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" @@ -849,6 +943,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" @@ -867,6 +976,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" @@ -875,6 +985,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" @@ -888,6 +999,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" @@ -896,16 +1008,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" @@ -914,11 +1026,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" @@ -939,7 +1049,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" @@ -947,7 +1057,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" @@ -967,31 +1077,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" @@ -1014,10 +1120,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" @@ -1025,62 +1131,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" @@ -1096,29 +1187,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" @@ -1141,19 +1227,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" @@ -1165,19 +1249,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" @@ -1210,14 +1292,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" @@ -1228,24 +1305,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" @@ -1259,76 +1331,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" @@ -1336,11 +1386,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" @@ -1348,42 +1398,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" @@ -1392,8 +1429,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" @@ -1403,8 +1440,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" @@ -1414,72 +1451,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" @@ -1495,12 +1511,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"; @@ -1518,18 +1531,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" @@ -1585,16 +1593,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" @@ -1607,11 +1612,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" @@ -1620,7 +1625,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" @@ -1631,11 +1636,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" @@ -1662,13 +1667,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" @@ -1692,7 +1695,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" @@ -1708,7 +1711,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" @@ -1725,9 +1728,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" @@ -1760,13 +1763,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" @@ -1776,7 +1779,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" @@ -1799,7 +1802,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"; @@ -1869,11 +1872,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" @@ -1891,20 +1894,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" @@ -1921,7 +1924,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" @@ -1935,16 +1938,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" @@ -1956,40 +2032,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"; @@ -2008,35 +2198,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" @@ -2046,10 +2226,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" @@ -2075,7 +2261,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" @@ -2086,9 +2272,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" @@ -2110,13 +2296,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"; @@ -2136,36 +2320,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" @@ -2176,9 +2352,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" @@ -2263,9 +2439,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"; @@ -2287,19 +2461,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" @@ -2314,6 +2484,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" @@ -2333,8 +2504,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" @@ -2345,23 +2517,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" @@ -2407,15 +2575,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" @@ -2437,7 +2603,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" @@ -2458,39 +2624,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" @@ -2511,21 +2668,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" @@ -2557,6 +2712,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" @@ -2575,32 +2817,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" @@ -2618,7 +2851,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" @@ -2631,12 +2864,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" @@ -2711,7 +2944,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" @@ -2737,36 +2970,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 = @@ -2783,72 +3060,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" @@ -2861,12 +3121,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" @@ -2878,7 +3137,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"; @@ -2896,109 +3160,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" @@ -3008,13 +3235,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 = null, $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" @@ -3026,7 +3258,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" @@ -3049,36 +3281,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" @@ -3160,6 +3389,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 = @@ -3180,19 +3439,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" @@ -3216,6 +3471,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" @@ -3360,66 +3621,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" @@ -3470,53 +3731,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" @@ -3555,15 +3814,17 @@ 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" + "\n" " /** @var callable */\n" " private $progressFunction;\n" "\n" @@ -3589,6 +3850,8 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private $resolve = [];\n" "\n" + " private $unix_socket_path = '';\n" + "\n" " public function __construct(string $url = '')\n" " {\n" " if ($url) {\n" @@ -3596,7 +3859,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" @@ -3631,14 +3894,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" @@ -3646,7 +3909,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" @@ -3659,12 +3922,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" @@ -3683,6 +3946,13 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $this->urlInfo['host'] = $host = $this->resolve[$host][$port] ?? null ?: $host;\n" " }\n" + " if ($this->unix_socket_path) {\n" + " $host = $this->unix_socket_path;\n" + " $port = 0;\n" + " if (stripos($host, 'unix:/') !== 0) {\n" + " $host = \"unix:/{$host}\";\n" + " }\n" + " }\n" " $this->client = new Client($host, $port, $urlInfo['scheme'] === 'https');\n" " }\n" "\n" @@ -3708,11 +3978,19 @@ 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) {\n" + " if (!str_contains($url, '://') && $this->unix_socket_path === '') {\n" " $url = 'http://' . $url;\n" " }\n" " if ($setInfo) {\n" " $urlInfo = parse_url($url);\n" + " if ($this->unix_socket_path) {\n" + " if (empty($urlInfo['host']) && !empty($urlInfo['path'])) {\n" + " $urlInfo['host'] = explode('/', $urlInfo['path'])[1] ?? null;\n" + " }\n" + " if (!$this->hasHeader('Host') && !empty($urlInfo['host'])) {\n" + " $this->setHeader('Host', $urlInfo['host']);\n" + " }\n" + " }\n" " if (!is_array($urlInfo)) {\n" " $this->setError(CURLE_URL_MALFORMAT, \"URL[{$url}] using bad/illegal format\");\n" " return false;\n" @@ -3745,7 +4023,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" @@ -3761,7 +4039,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" @@ -3771,7 +4049,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" @@ -3788,7 +4066,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" @@ -3797,10 +4075,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" @@ -3827,8 +4104,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" @@ -3850,9 +4127,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" @@ -3869,13 +4144,21 @@ 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" " case CURLOPT_PROXYAUTH:\n" " /* ignored temporarily */\n" " break;\n" + " case CURLOPT_UNIX_SOCKET_PATH:\n" + " $realpath = realpath((string) $value);\n" + " if ($realpath) {\n" + " $this->unix_socket_path = $realpath;\n" + " } else {\n" + " $this->setError(CURLE_COULDNT_CONNECT);\n" + " }\n" + " break;\n" " case CURLOPT_NOBODY:\n" " $this->nobody = boolval($value);\n" " $this->method = 'HEAD';\n" @@ -3888,9 +4171,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" @@ -3901,9 +4184,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" @@ -3912,18 +4193,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" @@ -3935,10 +4212,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" @@ -3961,9 +4239,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" @@ -3973,26 +4251,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" @@ -4031,9 +4309,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" @@ -4072,6 +4350,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_WRITEFUNCTION:\n" " $this->writeFunction = $value;\n" " break;\n" + " case CURLOPT_NOPROGRESS:\n" + " $this->noProgress = $value;\n" + " break;\n" " case CURLOPT_PROGRESSFUNCTION:\n" " $this->progressFunction = $value;\n" " break;\n" @@ -4109,7 +4390,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" @@ -4117,8 +4398,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" @@ -4126,21 +4407,21 @@ 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" - " do {\n" + " while (true) {\n" " $client = $this->client;\n" " /*\n" " * 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" @@ -4150,33 +4431,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" @@ -4210,7 +4486,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" @@ -4221,14 +4497,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" @@ -4253,11 +4529,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" @@ -4279,11 +4555,11 @@ static const char* swoole_library_source_core_curl_handler = " } else {\n" " break;\n" " }\n" - " } while (true);\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" + " }\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" @@ -4293,6 +4569,11 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['primary_ip'] = $this->urlInfo['host'];\n" " }\n" "\n" + " if ($this->unix_socket_path) {\n" + " $this->info['primary_ip'] = $this->unix_socket_path;\n" + " $this->info['primary_port'] = $this->urlInfo['port'];\n" + " }\n" + "\n" " $headerContent = '';\n" " if ($client->headers) {\n" " $cb = $this->headerFunction;\n" @@ -4331,7 +4612,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" @@ -4342,7 +4623,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" @@ -4357,7 +4638,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" @@ -4381,14 +4662,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" @@ -4402,7 +4683,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" @@ -4411,7 +4692,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" @@ -4543,63 +4824,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" @@ -4624,26 +4889,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" @@ -4660,11 +4928,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" @@ -4735,18 +5003,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" @@ -4783,22 +5051,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" @@ -4809,55 +5082,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" + " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" + " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\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" @@ -4865,6 +5160,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" @@ -4902,7 +5198,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" @@ -4937,10 +5233,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" @@ -4949,23 +5243,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" @@ -5003,13 +5293,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" @@ -5048,7 +5345,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" @@ -5080,10 +5377,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" @@ -5097,27 +5392,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" @@ -5153,13 +5441,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" @@ -5216,8 +5511,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" @@ -5264,8 +5561,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" @@ -5296,7 +5595,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" @@ -5327,7 +5626,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" @@ -5358,7 +5657,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" @@ -5394,22 +5693,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" @@ -5426,12 +5721,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" @@ -5456,9 +5760,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" @@ -5468,35 +5781,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" @@ -5507,28 +5822,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" @@ -5550,27 +5867,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" @@ -5581,13 +5895,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" @@ -5598,7 +5912,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" @@ -5609,7 +5923,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" @@ -5635,18 +5949,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" @@ -5657,9 +5971,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" @@ -5690,20 +6004,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" @@ -5718,9 +6034,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"; @@ -5738,30 +6057,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" @@ -5849,7 +6166,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" @@ -5975,7 +6293,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" @@ -6113,7 +6431,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" @@ -6128,14 +6446,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" @@ -6164,6 +6482,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" @@ -6174,19 +6495,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" @@ -6196,17 +6526,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" @@ -6214,8 +6544,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" @@ -6248,6 +6578,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" @@ -6255,11 +6588,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" @@ -6268,6 +6604,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" @@ -6294,7 +6633,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" @@ -6306,48 +6645,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" @@ -6361,33 +6695,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" @@ -6396,27 +6714,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" @@ -6425,15 +6740,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" @@ -6441,7 +6756,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" @@ -6451,7 +6767,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" @@ -6491,10 +6807,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" @@ -6525,8 +6842,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" @@ -6587,67 +6904,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" - " }\n" - " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" - " $queryString = $server['query_string'] ?? '';\n" - " if (strlen($queryString) !== 0) {\n" - " $requestUri .= \"?{$server['query_string']}\";\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" - " $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" @@ -6659,28 +6969,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"; @@ -6699,6 +7010,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" @@ -6781,7 +7093,7 @@ static const char* swoole_library_source_core_process_manager = " }\n" "}\n"; -static const char* swoole_library_source_core_server_helper = +static const char* swoole_library_source_core_server_admin = "\n" "/**\n" " * This file is part of Swoole.\n" @@ -6795,188 +7107,2303 @@ static const char* swoole_library_source_core_server_helper = "\n" "namespace Swoole\\Server;\n" "\n" + "use Swoole\\Coroutine;\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 Helper\n" + "class Admin\n" "{\n" - " const STATS_TIMER_INTERVAL_TIME = 1000;\n" - "\n" - " 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" - " ];\n" + " /**\n" + " * gdb php\n" + " * (gdb) p sizeof(zval)\n" + " * $2 = 16\n" + " * (gdb) p sizeof(zend_array)\n" + " * $1 = 56\n" + " * (gdb) p sizeof(zend_string)\n" + " * $3 = 32\n" + " * (gdb) p sizeof(zend_object)\n" + " * $4 = 56\n" + " */\n" + " public const SIZE_OF_ZVAL = 16;\n" + "\n" + " public const SIZE_OF_ZEND_STRING = 32;\n" + "\n" + " public const SIZE_OF_ZEND_OBJECT = 56;\n" "\n" - " 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" + " public const SIZE_OF_ZEND_ARRAY = 56;\n" + "\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" - " const PORT_OPTIONS = [\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" + " 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" - " const HELPER_OPTIONS = [\n" - " 'stats_file' => true,\n" + " private static array $postMethodList = [\n" + " 'server_reload',\n" + " 'server_shutdown',\n" + " 'close_session',\n" " ];\n" "\n" - " public static function checkOptions(array $input_options)\n" + " private static string $accessToken = '';\n" + "\n" + " public static function init(Server $server): void\n" " {\n" - " $const_options = self::GLOBAL_OPTIONS + self::SERVER_OPTIONS + self::PORT_OPTIONS + self::HELPER_OPTIONS;\n" + " $accepted_process_types = SWOOLE_SERVER_COMMAND_MASTER |\n" + " SWOOLE_SERVER_COMMAND_MANAGER |\n" + " SWOOLE_SERVER_COMMAND_EVENT_WORKER |\n" + " SWOOLE_SERVER_COMMAND_TASK_WORKER;\n" + "\n" + " $server->addCommand(\n" + " 'server_reload',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $server->reload();\n" + " return self::json('Operation succeeded');\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'server_shutdown',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg): void {\n" + " $server->shutdown();\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'coroutine_stats',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(Coroutine::stats())\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'coroutine_list',\n" + " $accepted_process_types,\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 $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" + " return self::json($bt);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'server_stats',\n" + " $accepted_process_types,\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 $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" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_client_info',\n" + " $accepted_process_types,\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" + " return self::json($server->getClientInfo(intval($json['session_id'])));\n" + " }\n" + " );\n" + "\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" + " [self::class, 'handlerGetServerMemoryUsage']\n" + " );\n" + " $server->addCommand(\n" + " 'get_static_property_value',\n" + " $accepted_process_types,\n" + " [self::class, 'handlerGetStaticPropertyValue']\n" + " );\n" + " $server->addCommand(\n" + " 'get_defined_functions',\n" + " $accepted_process_types,\n" + " [self::class, 'handlerGetDefinedFunctions']\n" + " );\n" + " $server->addCommand('get_declared_classes', $accepted_process_types, [self::class, 'handlerGetDeclaredClasses']);\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" + " fn (Server $server, string $msg) => self::json(opcache_get_status(true))\n" + " );\n" + " }\n" + "\n" + " $server->addCommand(\n" + " 'getpid',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'memory_usage',\n" + " $accepted_process_types,\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" + " fn (Server $server, string $msg) => self::json(['files' => get_included_files()])\n" + " );\n" + "\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 $server, string $msg) {\n" + " $constants = get_defined_constants();\n" + " foreach ($constants as $k => $c) {\n" + " if (is_resource($c)) {\n" + " unset($constants[$k]);\n" + " }\n" + " }\n" + " unset($constants['NULL'], $constants['NAN'], $constants['INF']);\n" + " return self::json($constants);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_loaded_extensions',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $extensions = get_loaded_extensions();\n" + " $list = [];\n" + " foreach ($extensions as $key => $extension) {\n" + " $ext = new \\ReflectionExtension($extension);\n" + " $list[$key] = [\n" + " 'id' => ++$key,\n" + " 'name' => $extension,\n" + " 'version' => (string) $ext->getVersion(),\n" + " ];\n" + " }\n" + " return self::json($list);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_declared_interfaces',\n" + " $accepted_process_types,\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" + " 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, 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" + "\n" + " if (!file_exists($json['filename'])) {\n" + " return self::json(\"{$json['filename']} not exist\", 4004);\n" + " }\n" + "\n" + " if (!in_array($json['filename'], get_included_files())) {\n" + " return self::json('no permission', 4003);\n" + " }\n" + "\n" + " return self::json(file_get_contents($json['filename']));\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_globals',\n" + " $accepted_process_types,\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" + " $other = [];\n" + " if ($type === 'object') {\n" + " $other = [\n" + " 'class_name' => $item::class,\n" + " 'object_id' => spl_object_id($item),\n" + " 'object_hash' => spl_object_hash($item),\n" + " ];\n" + " }\n" + " if ($type == 'resource' || $type == 'resource (closed)') {\n" + " $item = '';\n" + " }\n" + " $globals[] = [\n" + " 'key' => $key,\n" + " 'value' => $item,\n" + " 'type' => $type,\n" + " 'other' => $other,\n" + " ];\n" + " }\n" + " return self::json($globals);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_extension_info',\n" + " $accepted_process_types,\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" + "\n" + " ob_start();\n" + " $ext->info();\n" + " $info = ob_get_clean();\n" + "\n" + " $constants = $ext->getConstants();\n" + " foreach ($constants as $k => $c) {\n" + " if (is_resource($c)) {\n" + " unset($constants[$k]);\n" + " }\n" + " }\n" + "\n" + " 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" + " 'dependencies' => $ext->getDependencies(),\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 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" + " swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " return;\n" + " }\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" + " $path_array = swoole_string($req->server['request_uri'])->trim('/')->split('/');\n" + " if ($path_array->count() < 2 or $path_array->count() > 3) {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad API path', 4003));\n" + " 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" + " $process = $path_array->get(2);\n" + " }\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->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" + " _bad_process:\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad process', 4003));\n" + " return;\n" + " }\n" + "\n" + " if (!isset(self::$map[$array->get(0)->toString()])) {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " }\n" + "\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" + " ], JSON_THROW_ON_ERROR));\n" + " } else {\n" + " $resp->end($result);\n" + " }\n" + " });\n" + " $admin_server->handle('/', function (Request $req, Response $resp): void {\n" + " $resp->status(404);\n" + " });\n" + " $server->admin_server = $admin_server;\n" + " $admin_server->start();\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerGetResources(Server $server, string $msg)\n" + " {\n" + " $resources = get_resources();\n" + " $list = [];\n" + " foreach ($resources as $r) {\n" + " $info = [\n" + " 'id' => get_resource_id($r),\n" + " 'type' => get_resource_type($r),\n" + " ];\n" + " if ($info['type'] == 'stream') {\n" + " $info['info'] = stream_get_meta_data($r);\n" + " }\n" + " $list[] = $info;\n" + " }\n" + " return self::json($list);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\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" + " 'memory_real_usage' => memory_get_usage(true),\n" + " 'process_status' => self::getProcessStatus(),\n" + " 'coroutine_stats' => Coroutine::stats(),\n" + " 'timer_stats' => Timer::stats(),\n" + " 'vm_status' => swoole_get_vm_status(),\n" + " ];\n" + " return self::json($info);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerCloseSession(Server $server, string $msg)\n" + " {\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" + " if ($server->close(intval($json['session_id']), !empty($json['force']))) {\n" + " return self::json([]);\n" + " }\n" + " return self::json(['error' => swoole_last_error()], 4004);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\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" + " 'info' => Timer::info($timer_id),\n" + " ];\n" + " }\n" + "\n" + " return self::json($list);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\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" + " 'stack_usage' => Coroutine::getStackUsage($cid),\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 $server, string $msg)\n" + " {\n" + " $list = [];\n" + " $objects = swoole_get_objects();\n" + " foreach ($objects as $o) {\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" + "\n" + " return self::json($list);\n" + " }\n" + "\n" + " public static function handlerGetClassInfo(Server $server, string $msg)\n" + " {\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($name);\n" + "\n" + " $filename = $class->getFileName();\n" + "\n" + " $getTmpConstants = function ($data) {\n" + " $tmp = [];\n" + " foreach ($data as $k => $v) {\n" + " $tmp[] = [\n" + " 'name' => $k,\n" + " 'value' => is_array($v) ? var_export($v, true) : $v,\n" + " 'type' => is_array($v) ? 'detail' : 'default',\n" + " ];\n" + " }\n" + " return $tmp;\n" + " };\n" + "\n" + " $tmpConstants = $class->getConstants();\n" + " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" + "\n" + " $staticProperties = [];\n" + " $properties = [];\n" + " $tmpProperties = $class->getProperties();\n" + "\n" + " $getTmpProperties = function ($class, $data) {\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" + " if ($v->isStatic()) {\n" + " $static[] = [\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " } else {\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " }\n" + " }\n" + " return ['static' => $static, 'no_static' => $noStatic];\n" + " };\n" + "\n" + " if ($tmpProperties) {\n" + " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" + " $staticProperties = $tmpProperties['static'];\n" + " $properties = $tmpProperties['no_static'];\n" + " }\n" + "\n" + " $staticMethods = [];\n" + " $methods = [];\n" + " $tmpStaticMethods = $class->getMethods();\n" + "\n" + " $getTmpMethods = function ($data) {\n" + " $static = [];\n" + " $noStatic = [];\n" + " foreach ($data as $k => $v) {\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" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " } else {\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " }\n" + " }\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" + " }\n" + "\n" + " $tmpParentClass = $class->getParentClass();\n" + " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" + "\n" + " $data = [\n" + " 'filename' => $filename,\n" + " 'constants' => $constants,\n" + " 'staticProperties' => $staticProperties,\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 $server, string $msg)\n" + " {\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" + "\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" + "\n" + " $result = [\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 = $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 $reflection */\n" + " $reflection = $param->getType();\n" + " if ($reflection instanceof \\ReflectionUnionType) {\n" + " $unionType = [];\n" + " foreach ($reflection->getTypes() as $objType) {\n" + " $unionType[] = $objType->getName();\n" + " }\n" + " $type = implode('|', $unionType);\n" + " } else {\n" + " $type = $reflection->getName();\n" + " }\n" + " }\n" + "\n" + " if ($param->isOptional() && !$param->isVariadic()) {\n" + " $optional = '?';\n" + " if ($param->isDefaultValueAvailable()) {\n" + " $value = $param->getDefaultValue();\n" + " if (in_array($value, [true, false, null, ''])) {\n" + " if ($value === null) {\n" + " $value = 'null';\n" + " }\n" + " if ($value === true) {\n" + " $value = 'true';\n" + " }\n" + " if ($value === false) {\n" + " $value = 'false';\n" + " }\n" + " if ($value === '') {\n" + " $value = \"''\";\n" + " }\n" + " }\n" + " $default = \" = {$value}\";\n" + " }\n" + " }\n" + "\n" + " $isPassedByReference = $param->isPassedByReference() ? '&' : '';\n" + " $isVariadic = $param->isVariadic() ? '...' : '';\n" + "\n" + " $option = \"{$optional}{$type} {$isPassedByReference}{$isVariadic}\";\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 $server, string $msg)\n" + " {\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" + " $object = swoole_get_object_by_handle((int) $json['object_id']);\n" + " if (!$object) {\n" + " return self::json(['error' => 'Object destroyed!'], 4004);\n" + " }\n" + "\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" + "\n" + " return self::json(var_export($object, true));\n" + " }\n" + "\n" + " public static function handlerGetVersionInfo(Server $server, string $msg)\n" + " {\n" + " $ip_arr = swoole_get_local_ip();\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" + " } else {\n" + " $local[] = $ip;\n" + " }\n" + " }\n" + " $data = [\n" + " 'os' => php_uname('s') . '-' . php_uname('r'),\n" + " 'swoole' => swoole_version(),\n" + " 'php' => phpversion(),\n" + " 'ip' => $host ? $host[0] : $local[0],\n" + " ];\n" + " return self::json($data);\n" + " }\n" + "\n" + " public static function handlerGetDefinedFunctions(Server $server, string $msg)\n" + " {\n" + " $functions = get_defined_functions();\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" + " $arr['user'][] = [\n" + " 'function' => $function_name,\n" + " 'filename' => $filename,\n" + " 'line' => $line,\n" + " ];\n" + " }\n" + " }\n" + " return self::json($arr);\n" + " }\n" + "\n" + " public static function handlerGetDeclaredClasses(Server $server, string $msg)\n" + " {\n" + " $classes = get_declared_classes();\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" + " 'filename' => $filename ?: '',\n" + " 'line' => $line ?: '',\n" + " ];\n" + " }\n" + " }\n" + " return self::json($arr);\n" + " }\n" + "\n" + " public static function handlerGetServerMemoryUsage(Server $server, string $msg)\n" + " {\n" + " $total = 0;\n" + "\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" + " 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" + " $result[$key] = self::getProcessMemoryRealUsage($server->getWorkerPid($i));\n" + " $total += $result[$key];\n" + " }\n" + "\n" + " $result['total'] = $total;\n" + "\n" + " $result['memory_size'] = 0;\n" + " // 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'] = intval($match[1]) * 1024;\n" + " }\n" + "\n" + " return self::json($result);\n" + " }\n" + "\n" + " public static function handlerGetServerCpuUsage(Server $server, string $msg)\n" + " {\n" + " $total = 0;\n" + "\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" + " 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" + " $result[$key] = self::getProcessCpuUsage($server->getWorkerPid($i))[1] ?? 0;\n" + " $total += $result[$key];\n" + " }\n" + "\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 $server, string $msg)\n" + " {\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" + " if (empty($json['property_name'])) {\n" + " return self::json(['error' => 'require property_name!'], 4004);\n" + " }\n" + "\n" + " $className = $json['class_name'];\n" + " $propertyName = $json['property_name'];\n" + "\n" + " if (!class_exists($className)) {\n" + " return self::json(\"class[{$className}] not exists\", 4004);\n" + " }\n" + "\n" + " $reflection = new \\ReflectionClass($className);\n" + " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" + "\n" + " $result = [\n" + " 'value' => var_export($value, true),\n" + " ];\n" + " 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' || !file_exists(\"/proc/{$pid}/stat\")) {\n" + " return [0];\n" + " }\n" + "\n" + " $statAll = file_get_contents('/proc/stat');\n" + " $statProc = file_get_contents(\"/proc/{$pid}/stat\");\n" + "\n" + " $dataAll = preg_split(\"/[ \\t]+/\", $statAll, 6);\n" + " assert($dataAll[0] === 'cpu', '/proc/stat malformed');\n" + " $dataProc = preg_split(\"/[ \\t]+/\", $statProc, 15);\n" + "\n" + " if (isset($dataProc[13]) and isset($dataProc[14])) {\n" + " return [\n" + " (int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4],\n" + " (int) $dataProc[13] + (int) $dataProc[14],\n" + " ];\n" + " }\n" + " return [(int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4]];\n" + " }\n" + "\n" + " private static function getProcessMemoryRealUsage($pid = 'self')\n" + " {\n" + " $status = self::getProcessStatus($pid);\n" + " if (!is_array($status) || !isset($status['VmRSS'])) {\n" + " return 0;\n" + " }\n" + " return intval($status['VmRSS']) * 1024;\n" + " }\n" + "\n" + " private static function getProcessStatus($pid = 'self')\n" + " {\n" + " $array = [];\n" + " // TODO: Support other OS\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" + " foreach ($lines as $l) {\n" + " if (empty($l)) {\n" + " continue;\n" + " }\n" + " [$k, $v] = swoole_string($l)->split(':');\n" + " $array[$k] = trim($v);\n" + " }\n" + " return $array;\n" + " }\n" + "\n" + " private static function getArrayMemorySize(array $a): int\n" + " {\n" + " $size = self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_ARRAY;\n" + " foreach ($a as $k => $v) {\n" + " if (is_string($k)) {\n" + " $size += self::getStringMemorySize($k);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " if (is_string($v)) {\n" + " $size += self::getStringMemorySize($v);\n" + " } elseif (is_array($v)) {\n" + " $size += self::getArrayMemorySize($v);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " }\n" + " return $size;\n" + " }\n" + "\n" + " private static function getStringMemorySize(string $s): int\n" + " {\n" + " return self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_STRING + strlen($s);\n" + " }\n" + "\n" + " private static function getObjectMemorySize(object $o): int\n" + " {\n" + " $vars = get_object_vars($o);\n" + " $size = self::SIZE_OF_ZEND_OBJECT;\n" + "\n" + " foreach ($vars as $v) {\n" + " if (is_array($v)) {\n" + " $size += self::getArrayMemorySize($v);\n" + " } elseif (is_string($v)) {\n" + " $size += self::getStringMemorySize($v);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " }\n" + "\n" + " 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" + " if (empty($result)) {\n" + " return json_encode([\n" + " 'code' => 5010,\n" + " 'data' => ['message' => json_last_error_msg(), 'code' => json_last_error()],\n" + " ]);\n" + " }\n" + " return $result;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_server_helper = + "\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\\Server;\n" + "\n" + "use Swoole\\Constant;\n" + "use Swoole\\Coroutine;\n" + "use Swoole\\Server;\n" + "use Swoole\\Timer;\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" + " '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" + " '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" + " 'kernel_socket_recv_buffer_size' => true,\n" + " 'kernel_socket_send_buffer_size' => 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" + " ];\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_timer_interval' => true,\n" + " 'admin_server' => true,\n" + " ];\n" + "\n" + " public static function checkOptions(array $input_options): void\n" + " {\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 onWorkerStart(Server $server, int $workerId)\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): void\n" + " {\n" + " if (isset($server->admin_server)) { // @phpstan-ignore isset.property\n" + " $server->admin_server->shutdown();\n" + " $server->admin_server = null; // @phpstan-ignore assign.propertyType\n" + " }\n" + " }\n" + "\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_file = swoole_string($server->setting['stats_file']);\n" + " if ($stats_file->endsWith('.json')) {\n" + " $out = json_encode($stats, JSON_THROW_ON_ERROR);\n" + " } elseif ($stats_file->endsWith('.php')) {\n" + " $out = \" $v) {\n" + " $lines[] = \"{$k}: {$v}\";\n" + " }\n" + " $out = implode(\"\\n\", $lines);\n" + " }\n" + " file_put_contents($server->setting['stats_file'], $out);\n" + " });\n" + " }\n" + " }\n" + "\n" + " public static function onWorkerExit(Server $server, int $workerId): void\n" + " {\n" + " if ($server->stats_timer) {\n" + " Timer::clear($server->stats_timer);\n" + " $server->stats_timer = null;\n" + " }\n" + " }\n" + "\n" + " public static function onWorkerStop(Server $server, int $workerId)\n" + " {\n" + " }\n" + "\n" + " public static function onStart(Server $server): void\n" + " {\n" + " if (!empty($server->setting[Constant::OPTION_ADMIN_SERVER])) {\n" + " Coroutine::create(function () use ($server): void {\n" + " Admin::start($server);\n" + " });\n" + " }\n" + " }\n" + "\n" + " public static function onShutdown(Server $server)\n" + " {\n" + " }\n" + "\n" + " public static function onBeforeReload(Server $server)\n" + " {\n" + " }\n" + "\n" + " public static function onAfterReload(Server $server)\n" + " {\n" + " }\n" + "\n" + " public static function onManagerStart(Server $server)\n" + " {\n" + " }\n" + "\n" + " public static function onManagerStop(Server $server)\n" + " {\n" + " }\n" + "\n" + " public static function onWorkerError(Server $server)\n" + " {\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver = + "\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;\n" + "\n" + "use Swoole\\Coroutine\\Http\\ClientProxy;\n" + "use Swoole\\Http\\Status;\n" + "use Swoole\\NameResolver\\Cluster;\n" + "use Swoole\\NameResolver\\Exception;\n" + "\n" + "abstract class NameResolver\n" + "{\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" + "\n" + " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" + "\n" + " abstract public function leave(string $name, string $ip, int $port): bool;\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" + "\n" + " public function getFilter()\n" + " {\n" + " return $this->filter_fn;\n" + " }\n" + "\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" - " if (!empty($server->setting['stats_file']) and $workerId == 0) {\n" - " $server->stats_timer = Timer::tick(self::STATS_TIMER_INTERVAL_TIME, function () use ($server) {\n" - " $stats = $server->stats();\n" - " $lines = [];\n" - " foreach ($stats as $k => $v) {\n" - " $lines[] = \"{$k}: {$v}\";\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" - " $out = implode(\"\\n\", $lines);\n" - " file_put_contents($server->setting['stats_file'], $out);\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 static function onWorkerExit(Server $server, int $workerId)\n" + " public function shutdown(): void\n" " {\n" - " if ($server->stats_timer) {\n" - " Timer::clear($server->stats_timer);\n" - " $server->stats_timer = null;\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" - " public static function onWorkerStop(Server $server, int $workerId)\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"; @@ -6998,7 +9425,7 @@ static const char* swoole_library_source_core_coroutine_functions = "\n" "function run(callable $fn, ...$args)\n" "{\n" - " $s = new Scheduler();\n" + " $s = new Scheduler();\n" " $options = Coroutine::getOptions();\n" " if (!isset($options['hook_flags'])) {\n" " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" @@ -7034,7 +9461,7 @@ static const char* swoole_library_source_core_coroutine_functions = "function parallel(int $n, callable $fn): void\n" "{\n" " $count = $n;\n" - " $wg = new WaitGroup($n);\n" + " $wg = new WaitGroup($n);\n" " while ($count--) {\n" " Coroutine::create(function () use ($fn, $wg) {\n" " $fn();\n" @@ -7044,13 +9471,27 @@ static const char* swoole_library_source_core_coroutine_functions = " $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" @@ -7061,7 +9502,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" @@ -7077,7 +9518,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" @@ -7128,46 +9569,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" @@ -7177,9 +9604,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" @@ -7219,7 +9646,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" @@ -7227,7 +9654,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" @@ -7238,7 +9665,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" @@ -7269,11 +9696,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" @@ -7290,7 +9721,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" @@ -7366,9 +9797,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" @@ -7376,7 +9807,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" @@ -7384,7 +9815,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" @@ -7394,22 +9825,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 = @@ -7424,11 +9883,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" @@ -7445,6 +9904,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" @@ -7467,33 +9957,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" @@ -7514,24 +10003,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') {\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 = @@ -7546,7 +10051,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" @@ -7573,7 +10078,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" @@ -7592,63 +10097,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_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 858f55a4605..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)) \ - { \ - swDebug("+----------+------------+-------------------------------------------------------+"); \ - swDebug("| P#%-6u | L%-9u | %-10u %42s |", number, SW_MYSQL_PACKET_HEADER_SIZE + length, length, title); \ - swHexDump(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); - swTraceLog(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); - swTraceLog(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; - swTraceLog( - 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); - swTraceLog( - 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); - swTraceLog(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 c6af140254c..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 @@ -135,10 +176,6 @@ enum php_swoole_fd_type { PHP_SWOOLE_FD_REDIS_CORO, PHP_SWOOLE_FD_POSTGRESQL, PHP_SWOOLE_FD_SOCKET, - /** - * for Co::fread/Co::fwrite - */ - PHP_SWOOLE_FD_CO_UTIL, PHP_SWOOLE_FD_CO_CURL, }; //--------------------------------------------------------- @@ -150,15 +187,23 @@ 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 swSocket_type php_swoole_socktype(long type) { - return (enum swSocket_type)(type & (~SW_FLAG_SYNC) & (~SW_FLAG_ASYNC) & (~SW_FLAG_KEEP) & (~SW_SOCK_SSL)); +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)); } 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; @@ -173,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); @@ -211,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); @@ -219,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); @@ -277,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); @@ -295,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) +#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 -#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); -#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------------------------------------ @@ -421,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) { @@ -446,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; @@ -497,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) @@ -527,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; @@ -593,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) \ @@ -634,9 +670,13 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon } \ } while (0) -#define SW_SET_CLASS_SERIALIZABLE(module, _serialize, _unserialize) \ - module##_ce->serialize = _serialize; \ - module##_ce->unserialize = _unserialize +#if PHP_VERSION_ID < 80100 +#define SW_SET_CLASS_NOT_SERIALIZABLE(module) \ + 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; +#endif #define sw_zend_class_clone_deny NULL #define SW_SET_CLASS_CLONEABLE(module, _clone_obj) module##_handlers.clone_obj = _clone_obj @@ -664,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); @@ -683,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; } @@ -707,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); @@ -750,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) { @@ -777,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); @@ -795,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; @@ -817,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); @@ -826,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 @@ -888,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)); } @@ -938,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); @@ -964,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; } @@ -975,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; } @@ -1020,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------------------------------------ @@ -1046,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)); @@ -1055,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); } @@ -1076,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 088a6b2ed04..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 | +----------------------------------------------------------------------+ */ @@ -27,45 +27,53 @@ //-------------------------------------------------------- enum php_swoole_server_callback_type { - SW_SERVER_CB_onStart, // master - SW_SERVER_CB_onShutdown, // master - SW_SERVER_CB_onWorkerStart, // worker(event & task) - SW_SERVER_CB_onWorkerStop, // worker(event & task) - SW_SERVER_CB_onBeforeReload, // manager - SW_SERVER_CB_onAfterReload, // manager - SW_SERVER_CB_onTask, // worker(task) - SW_SERVER_CB_onFinish, // worker(event & task) - SW_SERVER_CB_onWorkerExit, // worker(event) - SW_SERVER_CB_onWorkerError, // manager - SW_SERVER_CB_onManagerStart, // manager - SW_SERVER_CB_onManagerStop, // manager - SW_SERVER_CB_onPipeMessage, // worker(event & task) + SW_SERVER_CB_onStart, // master + SW_SERVER_CB_onBeforeShutdown, // master + SW_SERVER_CB_onShutdown, // master + SW_SERVER_CB_onWorkerStart, // worker(event & task) + SW_SERVER_CB_onWorkerStop, // worker(event & task) + SW_SERVER_CB_onBeforeReload, // manager + SW_SERVER_CB_onAfterReload, // manager + SW_SERVER_CB_onTask, // worker(task) + SW_SERVER_CB_onFinish, // worker(event & task) + SW_SERVER_CB_onWorkerExit, // worker(event) + SW_SERVER_CB_onWorkerError, // manager + SW_SERVER_CB_onManagerStart, // manager + SW_SERVER_CB_onManagerStop, // manager + SW_SERVER_CB_onPipeMessage, // worker(event & task) }; //-------------------------------------------------------- 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; @@ -74,29 +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::unordered_map *> send_coroutine_map; + 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() { @@ -113,30 +126,29 @@ struct ServerObject { void register_callback(); void on_before_start(); + void copy_setting(zval *zsetting); }; struct TaskCo { - FutureTask context; - int *list; + Coroutine *co; + TaskId *list; uint32_t count; zval *result; - TimerNode *timer; - ServerObject *server_object; }; - +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 *); @@ -145,3 +157,5 @@ 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); + 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 777fe5ff748..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 | +----------------------------------------------------------------------+ */ @@ -29,11 +29,11 @@ using swoole::Timer; using swoole::coroutine::Socket; struct DNSCacheEntity { - char address[16]; + char address[INET6_ADDRSTRLEN]; 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 ad6be3ec385..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,75 +126,42 @@ 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_SET_CLASS_SERIALIZABLE(swoole_atomic, zend_class_serialize_deny, zend_class_unserialize_deny); + 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_SET_CLASS_SERIALIZABLE(swoole_atomic_long, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); SW_SET_CLASS_CUSTOM_OBJECT(swoole_atomic_long, @@ -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 8c5ebee4c2b..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,9 +106,8 @@ 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_SET_CLASS_SERIALIZABLE(swoole_channel_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); SW_SET_CLASS_CUSTOM_OBJECT(swoole_channel_coro, diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 21e8e7c4ff7..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_SET_CLASS_SERIALIZABLE(swoole_client, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); @@ -324,6 +240,10 @@ void php_swoole_client_check_ssl_setting(Client *cli, zval *zset) { zend_long v = zval_get_long(ztmp); cli->ssl_context->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); } + if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { + zend::String str_v(ztmp); + cli->ssl_context->ciphers = str_v.to_std_string(); + } if (!cli->ssl_context->cert_file.empty() && cli->ssl_context->key_file.empty()) { php_swoole_fatal_error(E_ERROR, "ssl require key file"); return; @@ -366,7 +286,7 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { cli->open_length_check = zval_is_true(ztmp); if (zval_is_true(ztmp)) { - swMqtt_set_protocol(&cli->protocol); + swoole::mqtt::set_protocol(&cli->protocol); } } // open length check @@ -399,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; @@ -433,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; @@ -442,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; } @@ -488,7 +391,7 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { _open_tcp_nodelay: if (cli->socket->socket_type == SW_SOCK_TCP || cli->socket->socket_type == SW_SOCK_TCP6) { if (cli->socket->set_tcp_nodelay() < 0) { - swSysWarn("setsockopt(%d, TCP_NODELAY) failed", cli->socket->fd); + swoole_sys_warning("setsockopt(%d, TCP_NODELAY) failed", cli->socket->fd); } } } @@ -568,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) { @@ -594,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); @@ -631,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); @@ -670,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); @@ -705,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", @@ -729,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; } @@ -746,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) { @@ -826,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; } @@ -854,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; } @@ -898,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 @@ -952,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; } @@ -995,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; } @@ -1007,7 +898,7 @@ static PHP_METHOD(swoole_client, recv) { cli->buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, sw_zend_string_allocator()); } - swString *buffer = cli->buffer; + String *buffer = cli->buffer; ssize_t eof = -1; char *buf = nullptr; @@ -1025,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()); @@ -1088,7 +978,7 @@ static PHP_METHOD(swoole_client, recv) { } else { cli->buffer->clear(); } - swString *buffer = cli->buffer; + String *buffer = cli->buffer; uint32_t header_len = protocol->package_length_offset + protocol->package_length_size; @@ -1098,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) { @@ -1150,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()); @@ -1184,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; } @@ -1221,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; } @@ -1240,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; } @@ -1321,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; + } + + 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_TRUE; + 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; } @@ -1361,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; } @@ -1378,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; } @@ -1396,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 45746a42bcf..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_SET_CLASS_SERIALIZABLE(swoole_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + 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,294 +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 swSocket_type 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; - } + 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; } -#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 (!sock->ssl_check_context()) { - ret = false; - } - return ret; -} -#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, @@ -486,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) @@ -507,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; } @@ -534,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; } @@ -580,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; @@ -646,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; @@ -689,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)) { @@ -702,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; @@ -718,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; @@ -746,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(); @@ -768,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 { @@ -788,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 { @@ -797,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; } @@ -813,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()); } @@ -822,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; } @@ -861,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())) { @@ -906,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 4ca574002b5..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; @@ -94,6 +94,7 @@ SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_coroutine, exists); static PHP_METHOD(swoole_coroutine, yield); static PHP_METHOD(swoole_coroutine, resume); +static PHP_METHOD(swoole_coroutine, join); static PHP_METHOD(swoole_coroutine, cancel); static PHP_METHOD(swoole_coroutine, isCanceled); static PHP_METHOD(swoole_coroutine, stats); @@ -103,110 +104,63 @@ static PHP_METHOD(swoole_coroutine, getContext); static PHP_METHOD(swoole_coroutine, getBackTrace); static PHP_METHOD(swoole_coroutine, printBackTrace); static PHP_METHOD(swoole_coroutine, getElapsed); +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_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() - 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, 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, 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 @@ -217,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; @@ -233,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) { @@ -265,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(); @@ -282,7 +283,7 @@ static int coro_end_silence_handler(zend_execute_data *execute_data) { static void coro_interrupt_resume(void *data) { Coroutine *co = (Coroutine *) data; if (co && !co->is_end()) { - swTraceLog(SW_TRACE_COROUTINE, "interrupt_callback cid=%ld ", co->get_cid()); + swoole_trace_log(SW_TRACE_COROUTINE, "interrupt_callback cid=%ld ", co->get_cid()); co->resume(); } } @@ -298,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() { @@ -309,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(); @@ -327,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(); @@ -366,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(); @@ -390,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() { @@ -434,41 +494,14 @@ void PHPCoroutine::interrupt_thread_start() { zend_vm_interrupt = &EG(vm_interrupt); interrupt_thread_running = true; interrupt_thread = std::thread([]() { - swSignal_none(); + 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 * @@ -478,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; } } @@ -571,48 +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); - swTraceLog( - SW_TRACE_COROUTINE, "php_coro_yield from cid=%ld to cid=%ld", task->co->get_cid(), task->co->get_origin_cid()); - 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 (ctx->on_yield) { + (*ctx->on_yield)(ctx); + } + + 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); - swTraceLog(SW_TRACE_COROUTINE, - "php_coro_resume from cid=%ld to cid=%ld", - Coroutine::get_current_cid(), - task->co->get_cid()); + 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 (ctx->on_resume) { + (*ctx->on_resume)(ctx); + } + + 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) { @@ -627,193 +677,121 @@ 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 - vm_stack_destroy(); - restore_task(origin_task); - - swTraceLog(SW_TRACE_COROUTINE, - "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", - cid, - origin_cid, - (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 - int i; - 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); + 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; + } - 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)); + // Release resources + if (ctx->context) { + zend_object *context = ctx->context; + ctx->context = (zend_object *) ~0; + OBJ_RELEASE(context); + } -#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 + Z_TRY_DELREF(ctx->fci.function_name); + ZVAL_UNDEF(&ctx->fci.function_name); + sw_zend_fci_cache_discard(&ctx->fci_cache); - for (i = 0; i < argc; ++i) { - 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); - } + Z_TRY_DELREF(ctx->return_value); - call->symbol_table = nullptr; +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(ctx, origin_ctx); + fiber_context_try_destroy(ctx); +#endif - 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); - } + swoole_trace_log(SW_TRACE_COROUTINE, + "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", + cid, + origin_cid, + (uintmax_t) Coroutine::count() - 1, + (uintmax_t) zend_memory_usage(0), + (uintmax_t) zend_memory_usage(1)); -#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 + zend_vm_stack_destroy(); + restore_context(origin_ctx); +} - 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->enable_scheduler = true; - - save_vm_stack(task); - record_last_msec(task); - - swTraceLog(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(), - (uintmax_t) Coroutine::count(), - (uintmax_t) zend_memory_usage(0)); - - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_CORO_START]) { - swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, 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", + ctx->co->get_cid(), + ctx->co->get_origin_cid(), + (uintmax_t) Coroutine::count(), + (uintmax_t) zend_memory_usage(0)); + + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_START)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx); } - 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); +#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 (task->defer_tasks) { - std::stack *tasks = task->defer_tasks; + // Catch exception in main function of the coroutine + exception_caught = catch_exception(); + + // 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; @@ -836,51 +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 PHPCoroutine::yield_m(zval *return_value, FutureTask *task) { - PHPContext *ctx = get_context(); - task->current_coro_return_value_ptr = return_value; - task->current_task = ctx; - on_yield(ctx); - ctx->co->yield_naked(); +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + +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::resume_m(FutureTask *task, zval *retval) { - PHPContext *ctx = task->current_task; - on_resume(ctx); - if (retval) { - ZVAL_COPY(task->current_coro_return_value_ptr, retval); +void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; } - ctx->co->resume_naked(); + fiber_context_init(ctx); } -void php_swoole_coroutine_minit(int module_number) { - PHPCoroutine::init(); +void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { + zend_observer_fiber_destroy_notify(ctx->fiber_context); - SW_INIT_CLASS_ENTRY_BASE( - swoole_coroutine_util, "Swoole\\Coroutine", nullptr, "Co", swoole_coroutine_methods, nullptr); + 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); @@ -890,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); @@ -914,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(); } @@ -946,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 { @@ -973,16 +1111,18 @@ PHP_FUNCTION(swoole_coroutine_defer) { static PHP_METHOD(swoole_coroutine, stats) { array_init(return_value); - add_assoc_long_ex(return_value, ZEND_STRL("event_num"), sw_reactor() ? sw_reactor()->event_num : 0); + add_assoc_long_ex(return_value, ZEND_STRL("event_num"), sw_reactor() ? sw_reactor()->get_event_num() : 0); add_assoc_long_ex( return_value, ZEND_STRL("signal_listener_num"), SwooleTG.signal_listener_num + SwooleTG.co_signal_listener_num); if (SwooleTG.async_threads) { - add_assoc_long_ex(return_value, ZEND_STRL("aio_task_num"), SwooleTG.async_threads->task_num); - add_assoc_long_ex(return_value, ZEND_STRL("aio_worker_num"), SwooleTG.async_threads->thread_count()); + add_assoc_long_ex(return_value, ZEND_STRL("aio_task_num"), SwooleTG.async_threads->get_task_num()); + add_assoc_long_ex(return_value, ZEND_STRL("aio_worker_num"), SwooleTG.async_threads->get_worker_num()); + add_assoc_long_ex(return_value, ZEND_STRL("aio_queue_size"), SwooleTG.async_threads->get_queue_size()); } else { add_assoc_long_ex(return_value, ZEND_STRL("aio_task_num"), 0); add_assoc_long_ex(return_value, ZEND_STRL("aio_worker_num"), 0); + add_assoc_long_ex(return_value, ZEND_STRL("aio_queue_size"), 0); } add_assoc_long_ex(return_value, ZEND_STRL("c_stack_size"), Coroutine::get_stack_size()); add_assoc_long_ex(return_value, ZEND_STRL("coroutine_num"), Coroutine::count()); @@ -1019,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) { @@ -1051,6 +1191,21 @@ static PHP_METHOD(swoole_coroutine, getElapsed) { RETURN_LONG(ret); } +static PHP_METHOD(swoole_coroutine, getStackUsage) { + zend_long cid = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ssize_t usage = PHPCoroutine::get_stack_usage(cid); + if (usage < 0) { + RETURN_FALSE; + } + RETURN_LONG(usage); +} + static PHP_METHOD(swoole_coroutine, exists) { zend_long cid; @@ -1062,14 +1217,15 @@ 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()) { - php_swoole_fatal_error(E_WARNING, "you can not resume the coroutine which is in IO operation or non-existent"); + php_swoole_fatal_error(E_WARNING, "can not resume the coroutine which is in IO operation or non-existent"); RETURN_FALSE; } @@ -1098,12 +1254,90 @@ static PHP_METHOD(swoole_coroutine, yield) { RETURN_TRUE; } -static PHP_METHOD(swoole_coroutine, cancel) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { +static PHP_METHOD(swoole_coroutine, join) { + Coroutine *co = Coroutine::get_current_safe(); + zval *cid_array; + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(cid_array) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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 = [&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); + }; + + zval *zcid; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cid_array), zcid) { + long cid = zval_get_long(zcid); + if (co->get_cid() == cid) { + 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) { + 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)) { + if (!co_set.empty()) { + for (auto ctx : co_set) { + ctx->on_close = nullptr; + } + delete canceled; + } else { + *canceled = true; + } + RETURN_FALSE; + } + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_coroutine, cancel) { + 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) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); @@ -1165,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); @@ -1184,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); @@ -1212,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 e39a9cf74fd..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,10 +95,9 @@ 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_SERIALIZABLE(swoole_coroutine_scheduler, zend_class_serialize_deny, zend_class_unserialize_deny); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_scheduler); SW_SET_CLASS_CLONEABLE(swoole_coroutine_scheduler, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_coroutine_scheduler, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(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, int &event_num) { +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, int &event_n 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 55177dd4809..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,58 +21,40 @@ #include -using swoole::TimerNode; using swoole::Coroutine; +using swoole::Event; using swoole::PHPCoroutine; -using swoole::FutureTask; using swoole::Reactor; -using swoole::Event; +using swoole::String; +using swoole::TimerNode; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::String; - -struct TmpSocket { - FutureTask context; - swSocket socket; - zend_string *buf; - uint32_t nbytes; - TimerNode *timer; -}; 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); } @@ -74,305 +72,6 @@ PHP_METHOD(swoole_coroutine_system, sleep) { RETURN_BOOL(System::sleep(seconds) == 0); } -static int co_socket_onReadable(Reactor *reactor, Event *event) { - TmpSocket *sock = (TmpSocket *) event->socket->object; - FutureTask *context = &sock->context; - - zval result; - - swoole_event_del(event->socket); - - if (sock->timer) { - swoole_timer_del(sock->timer); - sock->timer = nullptr; - } - - ssize_t n = event->socket->read(ZSTR_VAL(sock->buf), sock->nbytes); - if (n < 0) { - ZVAL_FALSE(&result); - zend_string_free(sock->buf); - } else if (n == 0) { - ZVAL_EMPTY_STRING(&result); - zend_string_free(sock->buf); - } else { - ZSTR_VAL(sock->buf)[n] = 0; - ZSTR_LEN(sock->buf) = n; - ZVAL_STR(&result, sock->buf); - } - PHPCoroutine::resume_m(context, &result); - zval_ptr_dtor(&result); - efree(sock); - return SW_OK; -} - -static int co_socket_onWritable(Reactor *reactor, Event *event) { - TmpSocket *sock = (TmpSocket *) event->socket->object; - FutureTask *context = &sock->context; - - zval result; - - swoole_event_del(event->socket); - - if (sock->timer) { - swoole_timer_del(sock->timer); - sock->timer = nullptr; - } - - ssize_t n = event->socket->write(context->private_data, sock->nbytes); - if (n < 0) { - swoole_set_last_error(errno); - ZVAL_FALSE(&result); - } else { - ZVAL_LONG(&result, n); - } - PHPCoroutine::resume_m(context, &result); - efree(sock); - return SW_OK; -} - -static void co_socket_read(int fd, zend_long length, INTERNAL_FUNCTION_PARAMETERS) { - php_swoole_check_reactor(); - if (!swoole_event_isset_handler(PHP_SWOOLE_FD_SOCKET)) { - swoole_event_set_handler(PHP_SWOOLE_FD_CO_UTIL | SW_EVENT_READ, co_socket_onReadable); - swoole_event_set_handler(PHP_SWOOLE_FD_CO_UTIL | SW_EVENT_WRITE, co_socket_onWritable); - } - - TmpSocket *sock = (TmpSocket *) ecalloc(1, sizeof(TmpSocket)); - - sock->socket.fd = fd; - sock->socket.fd_type = (enum swFd_type) PHP_SWOOLE_FD_CO_UTIL; - sock->socket.object = sock; - - if (swoole_event_add(&sock->socket, SW_EVENT_READ) < 0) { - swoole_set_last_error(errno); - efree(sock); - RETURN_FALSE; - } - - sock->buf = zend_string_alloc(length + 1, 0); - sock->nbytes = length <= 0 ? SW_BUFFER_SIZE_STD : length; - - PHPCoroutine::yield_m(return_value, &sock->context); -} - -static void co_socket_write(int fd, char *str, size_t l_str, INTERNAL_FUNCTION_PARAMETERS) { - TmpSocket *sock; - ssize_t ret = write(fd, str, l_str); - if (ret < 0) { - if (errno == EAGAIN) { - goto _yield; - } - swoole_set_last_error(errno); - RETURN_FALSE; - } else { - RETURN_LONG(ret); - } - -_yield: - sock = (TmpSocket *) ecalloc(1, sizeof(TmpSocket)); - - sock->socket.fd = fd; - sock->socket.fd_type = (enum swFd_type) PHP_SWOOLE_FD_CO_UTIL; - sock->socket.object = sock; - - if (swoole_event_add(&sock->socket, SW_EVENT_WRITE) < 0) { - swoole_set_last_error(errno); - RETURN_FALSE; - } - - FutureTask *task = &sock->context; - task->private_data = str; - sock->nbytes = l_str; - PHPCoroutine::yield_m(return_value, task); -} - -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; - swTrace("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; - swTrace("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; - swTrace("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; @@ -429,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"); @@ -465,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"); @@ -537,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"); - 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); + 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; } - 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) { @@ -611,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) @@ -622,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; @@ -635,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 58c75793516..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,17 +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); 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: @@ -61,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 = (enum swFd_type) 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); + } - swTraceLog(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL]", handle, cp, 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; @@ -106,92 +166,143 @@ 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; - swTraceLog( - SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", "[ADD]", 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, 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; - } - } - 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) { - co = check_bound_co(); - ON_SCOPE_EXIT { - co = nullptr; - }; - - 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 { + 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()); + } + } + + co = check_bound_co(); co->yield_ex(-1); - if (co->is_canceled()) { + is_canceled = co->is_canceled(); + co = nullptr; + + if (is_canceled) { swoole_set_last_error(SW_ERROR_CO_CANCELED); break; } + 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(); + curl_multi_socket_action(multi_handle_, sockfd, bitmask, &running_handles_); + swoole_trace_log(SW_TRACE_CO_CURL, + "curl_multi_socket_action: handle=%p, sockfd=%d, bitmask=%d, running_handles_=%d", + handle, + sockfd, + bitmask, + running_handles_); if (running_handles_ == 0) { 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); - return co->is_canceled() ? CURLE_ABORTED_BY_CALLBACK : retval; + remove_handle(handle); + return is_canceled ? CURLE_ABORTED_BY_CALLBACK : retval; } CURLcode Multi::read_info() { @@ -208,7 +319,7 @@ CURLcode Multi::read_info() { curl_easy_cleanup." */ return message->data.result; default: - swWarn("CURLMSG default"); + swoole_warning("CURLMSG default"); break; } } @@ -217,11 +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=%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 */ @@ -232,13 +348,15 @@ int Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) { } long Multi::select(php_curlm *mh, double timeout) { - co = check_bound_co(); if (zend_llist_count(&mh->easyh) == 0) { return 0; } - ON_SCOPE_EXIT { - co = nullptr; - }; + + if (curl_multi_socket_all(multi_handle_, &running_handles_) != CURLM_OK) { + 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; @@ -247,11 +365,25 @@ long Multi::select(php_curlm *mh, double timeout) { continue; } Handle *handle = get_handle(ch->cp); - 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()); + } } - swTraceLog(SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); } } set_timer(); @@ -261,7 +393,11 @@ long Multi::select(php_curlm *mh, double timeout) { return 0; } + co = check_bound_co(); co->yield_ex(timeout); + co = nullptr; + + swoole_trace_log(SW_TRACE_CO_CURL, "yield timeout, count=%lu", zend_llist_count(&mh->easyh)); auto count = selector->active_handles.size(); @@ -272,24 +408,36 @@ 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) { - swTraceLog( - 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(); if (selector->timer_callback) { selector->timer_callback = false; - curl_multi_socket_action(multi_handle_, -1, 0, &running_handles_); - swTraceLog(SW_TRACE_CO_CURL, "socket_action[timer], running_handles=%d", running_handles_); + curl_multi_socket_action(multi_handle_, CURL_SOCKET_TIMEOUT, 0, &running_handles_); + swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[timer], running_handles=%d", running_handles_); } 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_); - swTraceLog(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(); @@ -297,45 +445,69 @@ long Multi::select(php_curlm *mh, double timeout) { return count; } -void Multi::callback(Handle *handle, int event_bitmask) { - swTraceLog(SW_TRACE_CO_CURL, "callback, handle=%p, event_bitmask=%d", handle, event_bitmask); +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; } - // for curl_multi_select if (selector.get()) { if (!handle) { selector->timer_callback = true; } - if (!co) { - if (handle) { - if (swoole_event_del(handle->socket) == SW_OK) { + } + if (!co) { + if (handle) { + for (auto it : handle->sockets) { + if (swoole_event_del(it.second->socket) == SW_OK) { event_count_--; } - } else { - del_timer(); } - return; - } - if (handle) { - selector->active_handles.insert(handle); + } else { + del_timer(); } - if (selector->defer_callback) { - return; - } - selector->defer_callback = true; - swoole_event_defer( - [this](void *data) { - selector->defer_callback = false; - co->resume(); - }, - nullptr); - } else { - co->resume(); + return; } + if (selector.get() && handle) { + selector->active_handles.insert(handle); + } + if (defer_callback) { + return; + } + defer_callback = true; + swoole_event_defer( + [this](void *data) { + defer_callback = false; + if (co) { + co->resume(); + } + }, + nullptr); } } // 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 1c0d9dea8c3..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)); } } @@ -241,7 +233,7 @@ int php_swoole_reactor_init() { } } if (!sw_reactor()) { - swTraceLog(SW_TRACE_PHP, "init reactor"); + swoole_trace_log(SW_TRACE_PHP, "init reactor"); if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { php_swoole_fatal_error(E_ERROR, "Unable to create event-loop reactor"); @@ -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) { - swSignalfd_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) { - swSignalfd_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 8417c729ee5..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; } @@ -172,22 +171,56 @@ class Client { uint32_t send_request(zval *zrequest); bool write_data(uint32_t stream_id, zval *zdata, bool end); bool send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len); - enum swReturn_code parse_frame(zval *return_value, bool pipeline_read = false); + 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_SET_CLASS_SERIALIZABLE(swoole_http2_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + 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_SET_CLASS_SERIALIZABLE(swoole_http2_request, zend_class_serialize_deny, zend_class_unserialize_deny); + 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_SET_CLASS_SERIALIZABLE(swoole_http2_response, zend_class_serialize_deny, zend_class_unserialize_deny); + 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; } -enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { - char *buf = client->get_read_buffer()->str; +ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { + 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 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swHttp2FrameTraceLog(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))); - swHttp2FrameTraceLog(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) { @@ -532,37 +536,36 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_ERROR; } } - swTraceLog(SW_TRACE_HTTP2, "setting: header_compression_table_max=%u", value); + swoole_trace_log(SW_TRACE_HTTP2, "setting: header_compression_table_max=%u", value); break; case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: remote_settings.max_concurrent_streams = value; - swTraceLog(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); + 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; - swTraceLog(SW_TRACE_HTTP2, "setting: init_send_window=%u", 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: remote_settings.max_frame_size = value; - swTraceLog(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); break; 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 } - swTraceLog(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); break; default: // disable warning and ignore it because some websites are not following http2 protocol totally - // swWarn("unknown option[%d]: %d", id, value); + // swoole_warning("unknown option[%d]: %d", id, value); break; } buf += sizeof(id) + sizeof(value); @@ -577,9 +580,10 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_WINDOW_UPDATE: { value = ntohl(*(uint32_t *) buf); - swHttp2FrameTraceLog(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 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } case SW_HTTP2_TYPE_PING: { - swHttp2FrameTraceLog(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,12 +610,11 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swHttp2FrameTraceLog(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", - server_last_stream_id, - value, - (int) (length - SW_HTTP2_GOAWAY_SIZE), - buf); + 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), + buf); // update goaway error code and error msg zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), value); @@ -624,7 +627,7 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_RST_STREAM: { value = ntohl(*(uint32_t *) (buf)); - swHttp2FrameTraceLog(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 @@ enum swReturn_code 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; - swHttp2FrameTraceLog(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,14 +648,14 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } default: { - swHttp2FrameTraceLog(recv, ""); + swoole_http2_frame_trace_log(""); } } Stream *stream = get_stream(stream_id); // The stream is not found or has closed if (stream == nullptr) { - swNotice("http2 stream#%d belongs to an unknown type or it never registered", stream_id); + swoole_notice("http2 stream#%d belongs to an unknown type or it never registered", stream_id); return SW_CONTINUE; } if (type == SW_HTTP2_TYPE_HEADERS) { @@ -668,7 +671,7 @@ enum swReturn_code Client::parse_frame(zval *return_value, bool pipeline_read) { #ifdef SW_HAVE_ZLIB if (stream->gzip) { if (php_swoole_zlib_decompress(&stream->gzip_stream, stream->gzip_buffer, buf, length) == SW_ERR) { - swWarn("decompress failed"); + swoole_warning("decompress failed"); return SW_ERROR; } stream->buffer->append(stream->gzip_buffer->str, stream->gzip_buffer->length); @@ -680,19 +683,19 @@ enum swReturn_code 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]; - swTraceLog(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; - - swTraceLog(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; @@ -924,15 +915,15 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { in += (size_t) rv; inlen -= (size_t) rv; - swTraceLog(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN "] %.*s[%lu]: %.*s[%lu]", - "HEADER", - (int) nv.namelen, - nv.name, - nv.namelen, - (int) nv.valuelen, - nv.value, - nv.valuelen); + swoole_trace_log(SW_TRACE_HTTP2, + "[" SW_ECHO_GREEN "] %.*s[%lu]: %.*s[%lu]", + "HEADER", + (int) nv.namelen, + nv.name, + nv.namelen, + (int) nv.valuelen, + nv.value, + nv.valuelen); if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { if (nv.name[0] == ':') { @@ -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 */ @@ -958,13 +949,13 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { * zlib decode */ if (Z_OK != inflateInit2(&stream->gzip_stream, MAX_WBITS + 16)) { - swWarn("inflateInit2() failed"); + swoole_warning("inflateInit2() failed"); return SW_ERR; } } 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,13 +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"); @@ -1103,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); @@ -1114,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); } @@ -1125,8 +1121,8 @@ bool Client::send_data(uint32_t stream_id, const char *p, size_t len, int flag) uint32_t send_len; char header[SW_HTTP2_FRAME_HEADER_SIZE]; while (len > 0) { - if (len > remote_settings.max_frame_size) { - send_len = remote_settings.max_frame_size; + if (len > local_settings.max_frame_size) { + send_len = local_settings.max_frame_size; send_flag = 0; } else { send_len = len; @@ -1145,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); @@ -1188,11 +1187,8 @@ uint32_t Client::send_request(zval *zrequest) { Http2::set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id); - swTraceLog(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; } @@ -1219,11 +1215,10 @@ uint32_t Client::send_request(zval *zrequest) { len = str_zpost_data.len(); } - swTraceLog(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; @@ -1239,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); @@ -1260,12 +1258,12 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { return false; } Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, len, flag, stream_id); - swTraceLog(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ",%s STREAM#%d] length=%zu", - Http2::get_type(SW_HTTP2_TYPE_DATA), - end ? " END," : "", - stream_id, - len); + swoole_trace_log(SW_TRACE_HTTP2, + "[" SW_ECHO_GREEN ",%s STREAM#%d] length=%zu", + Http2::get_type(SW_HTTP2_TYPE_DATA), + end ? " END," : "", + stream_id, + len); if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(formstr, len)) { smart_str_free(&formstr_s); return false; @@ -1274,12 +1272,12 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { } else { zend::String data(zdata); Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, data.len(), flag, stream_id); - swTraceLog(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ",%s STREAM#%d] length=%zu", - Http2::get_type(SW_HTTP2_TYPE_DATA), - end ? " END," : "", - stream_id, - data.len()); + swoole_trace_log(SW_TRACE_HTTP2, + "[" SW_ECHO_GREEN ",%s STREAM#%d] length=%zu", + Http2::get_type(SW_HTTP2_TYPE_DATA), + end ? " END," : "", + stream_id, + data.len()); if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(data.val(), data.len())) { return false; } @@ -1292,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); @@ -1302,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); } - swTraceLog(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; @@ -1325,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; @@ -1333,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) @@ -1343,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; @@ -1350,7 +1353,7 @@ static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool if (!h2c->recv_packet(timeout)) { RETURN_FALSE; } - enum swReturn_code ret = h2c->parse_frame(return_value, pipeline_read); + ReturnCode ret = h2c->parse_frame(return_value, pipeline_read); if (ret == SW_CONTINUE) { continue; } else if (ret == SW_READY) { @@ -1362,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()); @@ -1411,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()); } @@ -1421,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; @@ -1439,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; @@ -1448,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; @@ -1478,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; @@ -1487,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 414f954dc6e..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; - send_window = SW_HTTP2_DEFAULT_WINDOW_SIZE; - recv_window = 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() { @@ -57,7 +59,8 @@ Http2Stream::~Stream() { void Http2Stream::reset(uint32_t error_code) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE]; - swTraceLog(SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, error_code=%u", "RST_STREAM", id, error_code); + swoole_trace_log( + SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, error_code=%u", "RST_STREAM", id, error_code); *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(error_code); http2::set_frame_header(frame, SW_HTTP2_TYPE_RST_STREAM, SW_HTTP2_RST_STREAM_SIZE, 0, id); ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE); @@ -65,11 +68,11 @@ void Http2Stream::reset(uint32_t error_code) { Http2Session::Session(SessionId _fd) { fd = _fd; - header_table_size = SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE; - send_window = SW_HTTP2_DEFAULT_WINDOW_SIZE; - recv_window = SW_HTTP2_DEFAULT_WINDOW_SIZE; - max_concurrent_streams = SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS; - max_frame_size = SW_HTTP2_MAX_MAX_FRAME_SIZE; + 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; @@ -94,13 +97,14 @@ Http2Session::~Session() { static void http2_server_send_window_update(HttpContext *ctx, uint32_t stream_id, uint32_t size) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE]; - swTraceLog(SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, size=%u", "WINDOW_UPDATE", stream_id, size); + swoole_trace_log( + SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, size=%u", "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); 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,25 +129,26 @@ 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) { - swWarn("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); + swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; } client->deflater = deflater; } 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) { - swWarn("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); + swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); return -1; } return 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,29 +418,27 @@ 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->header_table_size, php_nghttp2_mem()); + ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); if (ret != 0) { - swWarn("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); + swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; } client->deflater = deflater; @@ -366,7 +454,7 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l */ ssize_t rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, headers.get(), headers.len()); if (rv < 0) { - swWarn("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); + swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); 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,34 +529,54 @@ bool Http2Stream::send_header(size_t body_length, bool end_stream) { return true; } -bool Http2Stream::send_body(swString *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 flag = end_stream ? SW_HTTP2_FLAG_END_STREAM : SW_HTTP2_FLAG_NONE; + 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; - int _send_flag; - swoole_http_buffer->clear(); + int _send_flags; if (l > max_frame_size) { send_n = max_frame_size; - _send_flag = 0; + _send_flags = 0; } else { send_n = l; - _send_flag = flag; + _send_flags = flags; } - http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flag, id); - 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)) { - return false; + http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flags, id); + + // send twice to reduce memory copy + 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 { - l -= send_n; - p += send_n; + if (!ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE)) { + return false; + } + if (!ctx->send(ctx, p, send_n)) { + return false; + } } + + 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; } return true; @@ -476,15 +585,16 @@ bool Http2Stream::send_body(swString *body, bool end_stream, size_t max_frame_si 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; } } @@ -492,81 +602,115 @@ 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) { - if (body->length > client->send_window) { - swWarn("The data sent exceeded send_window"); + if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { + if (body->length > client->remote_window_size) { + swoole_warning("The data sent exceeded remote_window_size"); } - if (!stream->send_body(body, end_stream, client->max_frame_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; } - if (stream->send_window == 0) { + if (stream->remote_window_size == 0) { stream->waiting_coroutine = Coroutine::get_current(); stream->waiting_coroutine->yield(); stream->waiting_coroutine = nullptr; continue; - } else if (send_len <= stream->send_window) { - error = !stream->send_body(body, true && end_stream, client->max_frame_size, body->offset, send_len); - break; + } + + bool _end_stream; + if (send_len > stream->remote_window_size) { + send_len = stream->remote_window_size; + _end_stream = false; } else { - send_len = client->max_frame_size; - error = !stream->send_body(body, false, client->max_frame_size, body->offset, send_len); + _end_stream = true && end_stream; } - if (!error) { - swTraceLog(SW_TRACE_HTTP2, "body: send length=%zu", send_len); - body->offset += send_len; - if (send_len > stream->send_window) { - stream->send_window = 0; + 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); + + offset += send_len; + if (send_len > stream->remote_window_size) { + stream->remote_window_size = 0; } else { - stream->send_window -= send_len; + stream->remote_window_size -= send_len; } } } } - 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) { @@ -583,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; } @@ -606,36 +871,37 @@ 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; if (body->length > 0) { - if (!stream->send_body(body.get(), end_stream, client->max_frame_size, offset, length)) { + if (!stream->send_body(body.get(), end_stream, client->local_settings.max_frame_size, offset, length)) { error = true; } else { - client->send_window -= length; // TODO: flow control? + client->remote_window_size -= length; // TODO: flow control? } } @@ -646,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; @@ -655,13 +921,26 @@ 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) { int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem()); if (ret != 0) { - swWarn("nghttp2_hd_inflate_new2() failed, Error: %s[%d]", nghttp2_strerror(ret), ret); + swoole_warning("nghttp2_hd_inflate_new2() failed, Error: %s[%d]", nghttp2_strerror(ret), ret); return SW_ERR; } client->inflater = inflater; @@ -685,7 +964,7 @@ static int http2_parse_header(Http2Session *client, HttpContext *ctx, int flags, rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1); if (rv < 0) { - swWarn("inflate failed, Error: %s[%zd]", nghttp2_strerror(rv), rv); + swoole_warning("inflate failed, Error: %s[%zd]", nghttp2_strerror(rv), rv); return SW_ERR; } @@ -695,8 +974,12 @@ static int http2_parse_header(Http2Session *client, HttpContext *ctx, int flags, inlen -= proclen; if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - swTraceLog( - SW_TRACE_HTTP2, "Header: " SW_ECHO_BLUE "[%zu]: %s[%zu]", nv.name, nv.namelen, nv.value, nv.valuelen); + swoole_trace_log(SW_TRACE_HTTP2, + "name=(%zu)[" SW_ECHO_BLUE "], value=(%zu)[" SW_ECHO_CYAN "]", + nv.namelen, + nv.name, + nv.valuelen, + nv.value); if (nv.name[0] == ':') { if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "method")) { @@ -734,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) { - swWarn("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")) { @@ -801,49 +1086,49 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swHttp2FrameTraceLog(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))); - swHttp2FrameTraceLog(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->header_table_size) { - client->header_table_size = value; + if (value != client->remote_settings.header_table_size) { + client->remote_settings.header_table_size = value; if (client->deflater) { int ret = nghttp2_hd_deflate_change_table_size(client->deflater, value); if (ret != 0) { - swWarn("nghttp2_hd_deflate_change_table_size() failed, errno=%d, errmsg=%s", - ret, - nghttp2_strerror(ret)); + swoole_warning("nghttp2_hd_deflate_change_table_size() failed, errno=%d, errmsg=%s", + ret, + nghttp2_strerror(ret)); return SW_ERR; } } } - swTraceLog(SW_TRACE_HTTP2, "setting: header_table_size=%u", value); + swoole_trace_log(SW_TRACE_HTTP2, "setting: header_table_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - client->max_concurrent_streams = value; - swTraceLog(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); + client->remote_settings.max_concurrent_streams = value; + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); break; case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - client->send_window = value; - swTraceLog(SW_TRACE_HTTP2, "setting: init_send_window=%u", 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: - client->max_frame_size = value; - swTraceLog(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); + client->remote_settings.max_frame_size = value; + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - // client->max_header_list_size = value; // useless now - swTraceLog(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); + 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: // disable warning and ignore it because some websites are not following http2 protocol totally - // swWarn("unknown option[%d]: %d", id, value); + // swoole_warning("unknown option[%d]: %d", id, value); break; } buf += sizeof(id) + sizeof(value); @@ -853,7 +1138,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } case SW_HTTP2_TYPE_HEADERS: { stream = client->streams[stream_id]; - swHttp2FrameTraceLog(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); @@ -869,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; } @@ -881,7 +1166,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_DATA: { - swHttp2FrameTraceLog(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); @@ -893,7 +1178,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { zend_update_property_long( swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL("streamId"), stream_id); - swString *buffer = ctx->request.h2_data_buffer; + String *buffer = ctx->request.h2_data_buffer; if (!buffer) { buffer = new String(SW_HTTP2_DATA_BUFFER_SIZE); ctx->request.h2_data_buffer = buffer; @@ -901,16 +1186,19 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buffer->append(buf, length); // flow control - client->recv_window -= length; - stream->recv_window -= length; + client->local_window_size -= length; + stream->local_window_size -= length; + if (length > 0) { - if (client->recv_window < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, 0, SW_HTTP2_MAX_WINDOW_SIZE - client->recv_window); - client->recv_window = 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->recv_window < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->recv_window); - stream->recv_window = 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; } } @@ -922,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; } } @@ -939,7 +1224,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_PING: { - swHttp2FrameTraceLog(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( @@ -953,22 +1238,25 @@ 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->send_window += value; - } else if (client->streams.find(stream_id) != client->streams.end()) { - stream = client->streams[stream_id]; - Server *serv = (Server *) stream->ctx->private_data; - - stream->send_window += value; - if (serv->send_yield && stream->waiting_coroutine) { - stream->waiting_coroutine->resume(); + client->remote_window_size += value; + } else { + if (client->streams.find(stream_id) != client->streams.end()) { + stream = client->streams[stream_id]; + stream->remote_window_size += value; + if (!client->is_coro) { + Server *serv = (Server *) stream->ctx->private_data; + if (serv->send_yield && stream->waiting_coroutine) { + stream->waiting_coroutine->resume(); + } + } } } - swHttp2FrameTraceLog(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)); - swHttp2FrameTraceLog(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 @@ -983,36 +1271,32 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swHttp2FrameTraceLog(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", - server_last_stream_id, - value, - (int) (length - SW_HTTP2_GOAWAY_SIZE), - buf); + 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), + buf); // TODO: onRequest (void) server_last_stream_id; break; } default: { - swHttp2FrameTraceLog(recv, ""); + swoole_http2_frame_trace_log(""); } } return retval; } -/** - * Http2 - */ -int swoole_http2_server_onFrame(Server *serv, Connection *conn, RecvData *req) { +int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) { int session_id = req->info.fd; Http2Session *client = http2_sessions[session_id]; if (client == nullptr) { 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); @@ -1020,7 +1304,7 @@ int swoole_http2_server_onFrame(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; @@ -1048,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 34c2bfcca9c..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,15 +187,16 @@ 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) { - swString *buffer = nullptr; + String *buffer = nullptr; if (socket == nullptr) { if (tmp_write_buffer) { buffer = tmp_write_buffer; @@ -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; - swSocket_type 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()) { - swSysWarn("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); - return false; + swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); + return -1; } if (http->download_offset == 0) { if (!fp->truncate(0)) { - swSysWarn("ftruncate(%s) failed", download_file_name); - return false; + swoole_sys_warning("ftruncate(%s) failed", download_file_name); + return -1; } } else { - if (!fp->set_offest(http->download_offset)) { - swSysWarn("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); - return false; + if (!fp->set_offset(http->download_offset)) { + swoole_sys_warning("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); + 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; } @@ -651,7 +607,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { // gzip_stream.total_out = 0; status = inflateInit2(&gzip_stream, encoding); if (status != Z_OK) { - swWarn("inflateInit2() failed by %s", zError(status)); + swoole_warning("inflateInit2() failed by %s", zError(status)); return false; } gzip_stream_active = true; @@ -692,7 +648,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { goto _retry; } - swWarn("HttpClient::decompress_response failed by %s", zError(status)); + swoole_warning("HttpClient::decompress_response failed by %s", zError(status)); body->length = reserved_body_length; return false; } @@ -702,7 +658,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { if (!brotli_decoder_state) { brotli_decoder_state = BrotliDecoderCreateInstance(php_brotli_alloc, php_brotli_free, nullptr); if (!brotli_decoder_state) { - swWarn("BrotliDecoderCreateInstance() failed"); + swoole_warning("BrotliDecoderCreateInstance() failed"); return false; } } @@ -726,12 +682,12 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { return true; } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { if (!body->extend()) { - swWarn("BrotliDecoderDecompressStream() failed, no memory is available"); + swoole_warning("BrotliDecoderDecompressStream() failed, no memory is available"); break; } } else { - swWarn("BrotliDecoderDecompressStream() failed, %s", - BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); + swoole_warning("BrotliDecoderDecompressStream() failed, %s", + BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); break; } } @@ -739,17 +695,59 @@ 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; } - swWarn("HttpClient::decompress_response unknown compress method [%d]", compress_method); + swoole_warning("HttpClient::decompress_response unknown compress method [%d]", compress_method); return false; } #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,38 +957,36 @@ 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); // clear errno swoole_set_last_error(0); // alloc buffer - swString *buffer = socket->get_write_buffer(); + String *buffer = socket->get_write_buffer(); buffer->clear(); // clear body body->clear(); @@ -958,7 +1031,7 @@ bool HttpClient::send() { method = zbody ? "POST" : "GET"; method_len = strlen(method); } - this->method = swHttp_get_method(method, method_len); + this->method = http_server::get_method(method, method_len); buffer->append(method, method_len); buffer->append(ZEND_STRL(" ")); } @@ -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 ============ @@ -1332,133 +1417,61 @@ bool HttpClient::send() { } } - swTraceLog(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); + 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); 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; - swString *buffer = socket->get_read_buffer(); + String *buffer = socket->get_read_buffer(); bool header_completed = false; off_t header_crlf_offset = 0; @@ -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; @@ -1507,12 +1522,16 @@ bool HttpClient::recv_http_response(double timeout) { total_bytes += retval; parsed_n = swoole_http_parser_execute(&parser, &http_parser_settings, buffer->str, retval); - swTraceLog(SW_TRACE_HTTP_CLIENT, - "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", - parsed_n, - retval, - total_bytes, - parser.state == s_start_res); + 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); + 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; } - - swString *buffer = socket->get_write_buffer(); + 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_SERIALIZABLE(swoole_http_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + 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 4f4ca4e3f64..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_SET_CLASS_SERIALIZABLE(swoole_http_request, zend_class_serialize_deny, zend_class_unserialize_deny); + 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,132 +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) { - 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) { - swWarn("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) { - swWarn("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) { - swWarn( - "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) { - swWarn("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); +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; @@ -385,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); @@ -395,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) { - swWarn("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; - } - swTraceLog(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); - ctx->parse_form_data(boundary_str, boundary_len); + swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); + 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; } @@ -450,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; @@ -466,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; @@ -490,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; @@ -508,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); + 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) { - swWarn("form_name[%s] is too large", Z_STRVAL_P(zform_name)); + 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)); @@ -544,9 +528,9 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s // upload file else { if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) { - swWarn("filename[%s] is too large", Z_STRVAL_P(zfilename)); + 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; @@ -554,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)); @@ -567,43 +554,52 @@ 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; - swSysWarn("write upload file failed"); + swoole_sys_warning("write upload file failed"); } return 0; } @@ -648,21 +644,14 @@ static int multipart_body_on_header_complete(multipart_parser *p) { FILE *fp = fdopen(tmpfile, "wb+"); if (fp == nullptr) { add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_NO_TMP_DIR); - swSysWarn("fopen(%s) failed", file_path); + swoole_sys_warning("fopen(%s) failed", file_path); return 0; } 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; } @@ -673,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; } @@ -691,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) { @@ -757,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 { @@ -778,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; } } @@ -793,19 +783,30 @@ 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; - swTraceLog(SW_TRACE_HTTP, "request body length=%ld", content_length); + swoole_trace_log(SW_TRACE_HTTP, "request body length=%ld", content_length); return 1; /* return from execute */ } @@ -824,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; } @@ -839,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; @@ -846,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; @@ -858,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(); } @@ -874,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); @@ -990,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) { @@ -1001,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 f8edb299b2b..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,15 +29,14 @@ 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; 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++) { @@ -63,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) { @@ -89,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); - } } } } @@ -126,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; @@ -152,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_SET_CLASS_SERIALIZABLE(swoole_http_response, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); @@ -281,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", swHttp_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) { @@ -531,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; @@ -564,105 +503,128 @@ 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, input_buffer, &encoded_size, encoded_buffer)) { - swWarn("BrotliEncoderCompress() failed"); - return SW_ERR; + swoole_warning("BrotliEncoderCompress() failed"); + 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 { - swWarn("Unknown compression method"); - return SW_ERR; + swoole_warning("Unknown compression method"); + 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) { - swWarn("deflateInit2() failed, Error: [%d]", status); - return SW_ERR; + swoole_warning("deflateInit2() failed, Error: [%d]", status); + 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) { - swWarn("deflate() failed, Error: [%d]", status); - return SW_ERR; + swoole_warning("deflate() failed, Error: [%d]", status); + 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 @@ -703,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); } } @@ -717,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)) { @@ -727,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; @@ -752,63 +785,67 @@ 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; } - /** - * - */ -#ifdef SW_HTTP_SEND_TWICE - if (send_body_len < SwooleG.pagesize) #endif - { - if (http_buffer->append(send_body_str, send_body_len) < 0) { - send_header_ = 0; - RETURN_FALSE; - } - } -#ifdef SW_HTTP_SEND_TWICE - else { + // send twice to reduce memory copy + 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; + } } -#endif } if (!send(this, http_buffer->str, http_buffer->length)) { @@ -818,12 +855,11 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } -#ifdef SW_HTTP_SEND_TWICE _skip_copy: -#endif 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; @@ -842,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; } @@ -864,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; } @@ -894,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"); @@ -908,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; @@ -920,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) { @@ -1108,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; @@ -1165,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); @@ -1309,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 { @@ -1354,6 +1325,7 @@ static PHP_METHOD(swoole_http_response, create) { ctx->parser.data = ctx; swoole_http_parser_init(&ctx->parser, PHP_HTTP_REQUEST); } else { + delete ctx; assert(0); RETURN_FALSE; } @@ -1368,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; @@ -1379,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); @@ -1417,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 0d71470e924..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,28 +72,27 @@ 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_onFrame(serv, conn, req); + 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); - swTraceLog(SW_TRACE_SERVER, - "http request from %ld with %d bytes: <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,32 +100,58 @@ 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); - swNotice("request is illegal and it has been discarded, %ld bytes unprocessed", Z_STRLEN_P(zdata) - parsed_n); + swoole_notice("request is illegal and it has been discarded, %ld bytes unprocessed", + Z_STRLEN_P(zdata) - parsed_n); goto _dtor_and_return; } 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 { @@ -121,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: @@ -144,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_SET_CLASS_SERIALIZABLE(swoole_http_server, zend_class_serialize_deny, zend_class_unserialize_deny); + 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; @@ -185,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); @@ -204,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; @@ -211,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() { @@ -218,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; @@ -235,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; } @@ -267,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; @@ -280,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; @@ -294,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 78a63811127..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,8 +19,15 @@ #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; +using swoole::String; using swoole::coroutine::Socket; using swoole::coroutine::System; @@ -28,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; @@ -41,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 swSocket_type 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 @@ -90,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; @@ -117,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; @@ -136,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()); @@ -174,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); @@ -189,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; } @@ -264,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); @@ -299,25 +297,18 @@ 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_SERIALIZABLE(swoole_http_server_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server_coro); SW_SET_CLASS_CLONEABLE(swoole_http_server_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http_server_coro); 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); @@ -352,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) { @@ -396,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) { @@ -428,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; } @@ -451,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) { @@ -474,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)) { @@ -498,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; @@ -530,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()); - swString *buffer = sock->get_read_buffer(); + 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; @@ -576,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; - swTraceLog(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); @@ -627,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 { @@ -653,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; @@ -669,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; @@ -704,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)); } @@ -719,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 3420dda1de0..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_SET_CLASS_SERIALIZABLE(swoole_lock, zend_class_serialize_deny, zend_class_unserialize_deny); + 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 ff1b1159480..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 */ - swString *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; - swString *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) { - swTraceLog(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 { - swTraceLog(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); - swTraceLog(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) { - swTraceLog(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - RETURN_NULL(); - } else { - RETVAL_STRINGL(p, row_data->text.length); - _return: - swTraceLog(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: { - swWarn("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; - } - - swString *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) { - swTraceLog(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()); - swTraceLog(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()); - swTraceLog(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()); - swTraceLog(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)); - swTraceLog( - 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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(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); - swTraceLog(SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); - } break; - default: - swWarn("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_SERIALIZABLE(swoole_mysql_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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_SERIALIZABLE(swoole_mysql_coro_statement, zend_class_serialize_deny, zend_class_unserialize_deny); - 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_SERIALIZABLE(swoole_mysql_coro_exception, zend_class_serialize_deny, zend_class_unserialize_deny); - 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 50f3fa8d443..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 { - swWarn("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); - swTraceLog(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; - swTraceLog(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); - swTraceLog(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)); - swTraceLog(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", auth_plugin_name.c_str()); - } - } - swTraceLog(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); - swTraceLog(SW_TRACE_MYSQL_CLIENT, "Client capabilites=0x%08x", tint); - // max-packet size - tint = 300; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swTraceLog(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; - } - swTraceLog(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); - swTraceLog(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 - { - swWarn(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'; - swTraceLog(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)) { - swWarn("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)); - swWarn("[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; - swTraceLog(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)); - swWarn("[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")); - swTraceLog(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 91ee3695173..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,143 +130,46 @@ 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_SET_CLASS_SERIALIZABLE(swoole_process, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); SW_SET_CLASS_CUSTOM_OBJECT( @@ -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()->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) @@ -534,7 +469,7 @@ static PHP_METHOD(swoole_process, signal) { RETURN_FALSE; } - swSignalHandler handler = swSignal_get_handler(signo); + swSignalHandler handler = swoole_signal_get_handler(signo); if (handler && handler != php_swoole_onSignal) { php_swoole_fatal_error( E_WARNING, "signal [" ZEND_LONG_FMT "] processor has been registered by the system", signo); @@ -544,9 +479,13 @@ static PHP_METHOD(swoole_process, signal) { if (zcallback == nullptr) { fci_cache = signal_fci_caches[signo]; if (fci_cache) { - swSignal_set(signo, nullptr); +#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,52 +495,49 @@ 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; - swSignal_set(signo, handler); +#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, int &event_num) -> bool { + [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.signal_listener_num == 0 or !SwooleG.wait_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; - - swSignal_set(signo, handler); +#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; @@ -998,7 +933,7 @@ static PHP_METHOD(swoole_process, daemon) { int new_fd = php_swoole_convert_to_fd(elem); if (new_fd >= 0) { if (dup2(new_fd, fd) < 0) { - swSysWarn("dup2(%d, %d) failed", new_fd, fd); + swoole_sys_warning("dup2(%d, %d) failed", new_fd, fd); } } } @@ -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 eda655823cf..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,146 +122,192 @@ 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_SET_CLASS_SERIALIZABLE(swoole_process_pool, zend_class_serialize_deny, zend_class_unserialize_deny); + 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) { - swSignal_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: - current_pool->reloading = true; + current_pool->reload(); current_pool->reload_init = false; break; case SIGIO: @@ -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,54 +331,62 @@ 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)); *pool = {}; - if (pool->create(worker_num, (key_t) msgq_key, (swIPC_type) ipc_type) < 0) { + if (pool->create(worker_num, (key_t) msgq_key, (swIPCMode) ipc_type) < 0) { zend_throw_exception_ex(swoole_exception_ce, errno, "failed to create process pool"); efree(pool); RETURN_FALSE; } 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] = swSignal_set(SIGTERM, pool_signal_handler); - ori_handlers[SIGUSR1] = swSignal_set(SIGUSR1, pool_signal_handler); - ori_handlers[SIGUSR2] = swSignal_set(SIGUSR2, pool_signal_handler); - ori_handlers[SIGIO] = swSignal_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(); @@ -557,7 +638,7 @@ static PHP_METHOD(swoole_process_pool, start) { current_pool = nullptr; for (auto iter = ori_handlers.begin(); iter != ori_handlers.end(); iter++) { - swSignal_set(iter->first, iter->second); + swoole_signal_set(iter->first, iter->second); } } @@ -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 174201246c8..00000000000 --- a/ext-src/swoole_redis_coro.cc +++ /dev/null @@ -1,4514 +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() -// 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_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); - swTraceLog(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); -} - -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); -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) - /*---------------------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_SERIALIZABLE(swoole_redis_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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 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 eb144ef9481..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,17 +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); @@ -41,52 +47,34 @@ 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_SET_CLASS_SERIALIZABLE(swoole_redis_server, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("NIL"), SW_REDIS_REPLY_NIL); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("ERROR"), SW_REDIS_REPLY_ERROR); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("STATUS"), SW_REDIS_REPLY_STATUS); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("INT"), SW_REDIS_REPLY_INT); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("STRING"), SW_REDIS_REPLY_STRING); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("SET"), SW_REDIS_REPLY_SET); - zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("MAP"), SW_REDIS_REPLY_MAP); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("NIL"), Redis::REPLY_NIL); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("ERROR"), Redis::REPLY_ERROR); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("STATUS"), Redis::REPLY_STATUS); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("INT"), Redis::REPLY_INT); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("STRING"), Redis::REPLY_STRING); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("SET"), Redis::REPLY_SET); + zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL("MAP"), Redis::REPLY_MAP); } 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(); } @@ -95,7 +83,7 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { int fd = req->info.fd; Connection *conn = serv->get_connection_by_session_id(fd); if (!conn) { - swWarn("connection[%d] is closed", fd); + swoole_warning("connection[%d] is closed", fd); return SW_ERR; } @@ -115,38 +103,38 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { zval zparams{}; array_init(&zparams); - int state = SW_REDIS_RECEIVE_TOTAL_LINE; + int state = Redis::STATE_RECEIVE_TOTAL_LINE; int add_param = 0; const char *command = nullptr; int command_len = 0; do { switch (state) { - case SW_REDIS_RECEIVE_TOTAL_LINE: - if (*p == '*' && (p = swRedis_get_number(p, &ret))) { - state = SW_REDIS_RECEIVE_LENGTH; + case Redis::STATE_RECEIVE_TOTAL_LINE: + if (*p == '*' && (p = Redis::get_number(p, &ret))) { + state = Redis::STATE_RECEIVE_LENGTH; break; } /* no break */ - case SW_REDIS_RECEIVE_LENGTH: - if (*p == '$' && (p = swRedis_get_number(p, &ret))) { + case Redis::STATE_RECEIVE_LENGTH: + if (*p == '$' && (p = Redis::get_number(p, &ret))) { if (ret == -1) { add_next_index_null(&zparams); break; } length = ret; - state = SW_REDIS_RECEIVE_STRING; + state = Redis::STATE_RECEIVE_STRING; break; } // integer - else if (*p == ':' && (p = swRedis_get_number(p, &ret))) { + else if (*p == ':' && (p = Redis::get_number(p, &ret))) { add_next_index_long(&zparams, ret); break; } /* no break */ - case SW_REDIS_RECEIVE_STRING: + case Redis::STATE_RECEIVE_STRING: if (add_param == 0) { command = p; command_len = length; @@ -155,7 +143,7 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { add_next_index_stringl(&zparams, p, length); } p += length + SW_CRLF_LEN; - state = SW_REDIS_RECEIVE_LENGTH; + state = Redis::STATE_RECEIVE_LENGTH; break; default: @@ -171,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()) { @@ -180,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), @@ -210,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; } @@ -249,125 +242,136 @@ 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; - } - - char message[256]; - int length; - - swoole::String *format_buffer = sw_tg_buffer(); - - if (type == SW_REDIS_REPLY_NIL) { - RETURN_STRINGL(SW_REDIS_RETURN_NIL, sizeof(SW_REDIS_RETURN_NIL) - 1); - } else if (type == SW_REDIS_REPLY_STATUS) { - if (value) { - zend::String str_value(value); - length = sw_snprintf(message, sizeof(message), "+%.*s\r\n", (int) str_value.len(), str_value.val()); +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 { - length = sw_snprintf(message, sizeof(message), "+%s\r\n", "OK"); + redis_response_format(buf, Redis::REPLY_MAP, item); } - RETURN_STRINGL(message, length); - } else if (type == SW_REDIS_REPLY_ERROR) { + break; + default: + redis_response_format(buf, Redis::REPLY_STRING, item); + break; + } +} + +static bool redis_response_format(String *buf, zend_long type, zval *value) { + if (type == Redis::REPLY_NIL) { + 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", "ERR"); + SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message); } - RETURN_STRINGL(message, length); - } else if (type == SW_REDIS_REPLY_INT) { + } 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); - } else if (type == SW_REDIS_REPLY_STRING) { + 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); - } else if (type == SW_REDIS_REPLY_SET) { + 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; } 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); - } else if (type == SW_REDIS_REPLY_MAP) { + 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; } 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 3824e860e4d..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 { @@ -44,68 +51,66 @@ struct ServerEvent { // clang-format off static std::unordered_map server_event_map({ - { "start", ServerEvent(SW_SERVER_CB_onStart, "Start") }, - { "shutdown", ServerEvent(SW_SERVER_CB_onShutdown, "Shutdown") }, - { "workerstart", ServerEvent(SW_SERVER_CB_onWorkerStart, "WorkerStart") }, - { "workerstop", ServerEvent(SW_SERVER_CB_onWorkerStop, "WorkerStop") }, - { "beforereload", ServerEvent(SW_SERVER_CB_onBeforeReload, "BeforeReload") }, - { "afterreload", ServerEvent(SW_SERVER_CB_onAfterReload, "AfterReload") }, - { "task", ServerEvent(SW_SERVER_CB_onTask, "Task") }, - { "finish", ServerEvent(SW_SERVER_CB_onFinish, "Finish") }, - { "workerexit", ServerEvent(SW_SERVER_CB_onWorkerExit, "WorkerExit") }, - { "workererror", ServerEvent(SW_SERVER_CB_onWorkerError, "WorkerError") }, - { "managerstart", ServerEvent(SW_SERVER_CB_onManagerStart, "ManagerStart") }, - { "managerstop", ServerEvent(SW_SERVER_CB_onManagerStop, "ManagerStop") }, - { "pipemessage", ServerEvent(SW_SERVER_CB_onPipeMessage, "PipeMessage") }, + { "start", ServerEvent(SW_SERVER_CB_onStart, "Start") }, + { "beforeshutdown", ServerEvent(SW_SERVER_CB_onBeforeShutdown, "BeforeShutdown") }, + { "shutdown", ServerEvent(SW_SERVER_CB_onShutdown, "Shutdown") }, + { "workerstart", ServerEvent(SW_SERVER_CB_onWorkerStart, "WorkerStart") }, + { "workerstop", ServerEvent(SW_SERVER_CB_onWorkerStop, "WorkerStop") }, + { "beforereload", ServerEvent(SW_SERVER_CB_onBeforeReload, "BeforeReload") }, + { "afterreload", ServerEvent(SW_SERVER_CB_onAfterReload, "AfterReload") }, + { "task", ServerEvent(SW_SERVER_CB_onTask, "Task") }, + { "finish", ServerEvent(SW_SERVER_CB_onFinish, "Finish") }, + { "workerexit", ServerEvent(SW_SERVER_CB_onWorkerExit, "WorkerExit") }, + { "workererror", ServerEvent(SW_SERVER_CB_onWorkerError, "WorkerError") }, + { "managerstart", ServerEvent(SW_SERVER_CB_onManagerStart, "ManagerStart") }, + { "managerstop", ServerEvent(SW_SERVER_CB_onManagerStop, "ManagerStop") }, + { "pipemessage", ServerEvent(SW_SERVER_CB_onPipeMessage, "PipeMessage") }, }); // clang-format on -static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_task); -static void php_swoole_onPipeMessage(Server *serv, EventData *req); -static void php_swoole_onStart(Server *); -static void php_swoole_onShutdown(Server *); -static void php_swoole_server_onWorkerStart(Server *, int worker_id); +// server event callback +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 *, 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_onUserWorkerStart(Server *serv, Worker *worker); +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_onWorkerError(Server *serv, int worker_id, const ExitStatus &exit_status); +static void php_swoole_server_onPipeMessage(Server *serv, EventData *req); +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 void php_swoole_server_onSendTimeout(Timer *timer, TimerNode *tnode); -static enum swReturn_code php_swoole_server_send_resume(Server *serv, FutureTask *context, SessionId fd); -static void php_swoole_task_onTimeout(Timer *timer, TimerNode *tnode); +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,8 +165,35 @@ Server *php_swoole_server_get_and_check_server(zval *zobject) { return serv; } +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(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); } @@ -170,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; } } @@ -193,10 +227,19 @@ static void server_free_object(zend_object *object) { server_object->serv = nullptr; } + for (auto fci_cache : property->command_callbacks) { + 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; } } @@ -290,179 +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_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_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_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); @@ -472,6 +342,7 @@ static PHP_METHOD(swoole_server, getCallback); static PHP_METHOD(swoole_server, listen); static PHP_METHOD(swoole_server, sendMessage); static PHP_METHOD(swoole_server, addProcess); +static PHP_METHOD(swoole_server, addCommand); static PHP_METHOD(swoole_server, start); static PHP_METHOD(swoole_server, stop); static PHP_METHOD(swoole_server, send); @@ -493,6 +364,7 @@ static PHP_METHOD(swoole_server, finish); static PHP_METHOD(swoole_server, reload); static PHP_METHOD(swoole_server, shutdown); static PHP_METHOD(swoole_server, heartbeat); +static PHP_METHOD(swoole_server, command); static PHP_METHOD(swoole_server, getClientList); static PHP_METHOD(swoole_server, getClientInfo); static PHP_METHOD(swoole_server, getWorkerId); @@ -525,105 +397,104 @@ 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_void, 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, 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_SET_CLASS_SERIALIZABLE(swoole_server, zend_class_serialize_deny, zend_class_unserialize_deny); + 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_SERIALIZABLE(swoole_server_task, zend_class_serialize_deny, zend_class_unserialize_deny); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_task); SW_SET_CLASS_CLONEABLE(swoole_server_task, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_task, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_task, @@ -652,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------------------------------------- @@ -668,12 +540,9 @@ 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_SET_CLASS_SERIALIZABLE(swoole_connection_iterator, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); SW_SET_CLASS_CUSTOM_OBJECT(swoole_connection_iterator, @@ -687,6 +556,7 @@ void php_swoole_server_minit(int module_number) { #endif // ---------------------------------------Server Property------------------------------------- zend_declare_property_null(swoole_server_ce, ZEND_STRL("onStart"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onBeforeShutdown"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_ce, ZEND_STRL("onShutdown"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerStart"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerStop"), ZEND_ACC_PRIVATE); @@ -705,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); @@ -713,10 +584,47 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_bool(swoole_server_ce, ZEND_STRL("taskworker"), 0, ZEND_ACC_PUBLIC); 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 - SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_DISCARD_PACKET", SW_DISPATCH_RESULT_DISCARD_PACKET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_CLOSE_CONNECTION", SW_DISPATCH_RESULT_CLOSE_CONNECTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_USERFUNC_FALLBACK", SW_DISPATCH_RESULT_USERFUNC_FALLBACK); + /** + * 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 + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_UNSOCK", Server::TASK_IPC_UNIXSOCK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_MSGQUEUE", Server::TASK_IPC_MSGQUEUE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_PREEMPTIVE", Server::TASK_IPC_PREEMPTIVE); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_MASTER", Server::Command::MASTER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_MANAGER", Server::Command::MANAGER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_REACTOR_THREAD", Server::Command::REACTOR_THREAD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_EVENT_WORKER", Server::Command::EVENT_WORKER); + 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); SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_TMPFILE", SW_TASK_TMPFILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_SERIALIZE", SW_TASK_SERIALIZE); @@ -732,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]; } } @@ -755,22 +662,17 @@ 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; - 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(); - swTask_type(task) = 0; - + bool serialized = false; char *task_data_str; size_t task_data_len = 0; // need serialize if (Z_TYPE_P(zdata) != IS_STRING) { // serialize - swTask_type(task) |= SW_TASK_SERIALIZE; + serialized = true; PHP_VAR_SERIALIZE_INIT(var_hash); php_var_serialize(&serialized_data, zdata, &var_hash); @@ -786,12 +688,16 @@ TaskId php_swoole_task_pack(EventData *task, zval *zdata) { task_data_len = Z_STRLEN_P(zdata); } - if (!task->pack(task_data_str, task_data_len)) { + if (!Server::task_pack(task, task_data_str, task_data_len)) { php_swoole_fatal_error(E_WARNING, "large task pack failed"); task->info.fd = SW_ERR; task->info.len = 0; } + if (serialized) { + task->info.ext_flags |= SW_TASK_SERIALIZE; + } + smart_str_free(&serialized_data); return task->info.fd; } @@ -804,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->pop_worker_buffer(&req->info); + 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); @@ -814,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; @@ -830,104 +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; - char *result_data_str; - size_t result_data_len = 0; +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) { php_unserialize_data_t var_hash; + PacketPtr packet; - /** - * Large result package - */ - if (swTask_type(task_result) & SW_TASK_TMPFILE) { - if (!task_result->unpack(sw_tg_buffer())) { - return nullptr; - } - result_data_str = sw_tg_buffer()->str; - result_data_len = sw_tg_buffer()->length; - } else { - result_data_str = task_result->data; - result_data_len = task_result->info.len; + if (!Server::task_unpack(task_result, sw_tg_buffer(), &packet)) { + return false; } - if (swTask_type(task_result) & SW_TASK_SERIALIZE) { - result_unserialized_data = sw_malloc_zval(); - + if (task_result->info.ext_flags & SW_TASK_SERIALIZE) { + 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); - } else { - result_data = sw_malloc_zval(); - ZVAL_STRINGL(result_data, result_data_str, result_data_len); - } - return result_data; -} - -static void php_swoole_task_wait_co( - Server *serv, EventData *req, double timeout, int dst_worker_id, INTERNAL_FUNCTION_PARAMETERS) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); - swTask_type(req) |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); - - TaskCo *task_co = (TaskCo *) emalloc(sizeof(TaskCo)); - sw_memset_zero(task_co, sizeof(*task_co)); - task_co->count = 1; - Z_LVAL(task_co->context.coro_params) = req->info.fd; - - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch(req, &dst_worker_id) < 0) { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - RETURN_FALSE; - } else { - server_object->property->task_coroutine_map[req->info.fd] = task_co; - } - - long ms = (long) (timeout * 1000); - TimerNode *timer = swoole_timer_add(ms, false, php_swoole_task_onTimeout, task_co); - if (timer) { - task_co->timer = timer; - } - task_co->server_object = server_object; - PHPCoroutine::yield_m(return_value, &task_co->context); -} - -static void php_swoole_task_onTimeout(Timer *timer, TimerNode *tnode) { - TaskCo *task_co = (TaskCo *) tnode->data; - FutureTask *context = &task_co->context; - - // Server->taskwait, single task - if (task_co->list == nullptr) { - zval result; - ZVAL_FALSE(&result); - PHPCoroutine::resume_m(context, &result); - task_co->server_object->property->task_coroutine_map.erase(Z_LVAL(context->coro_params)); - efree(task_co); - return; - } - - uint32_t i; - zval *result = task_co->result; - - for (i = 0; i < task_co->count; i++) { - if (!zend_hash_index_exists(Z_ARRVAL_P(result), i)) { - add_index_bool(result, i, 0); - task_co->server_object->property->task_coroutine_map.erase(task_co->list[i]); + 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 { + ZVAL_STRINGL(zresult, packet.data, packet.length); } - PHPCoroutine::resume_m(context, result); - sw_zval_free(result); - efree(task_co); + return true; } extern ListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject); @@ -935,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); @@ -952,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); @@ -981,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 @@ -990,16 +833,16 @@ 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 - swTraceLog(SW_TRACE_SERVER, - "Create Server: host=%s, port=%d, mode=%d, type=%d", - primary_port->host.c_str(), - (int) primary_port->port, - serv->is_base_mode() ? Server::MODE_BASE : Server::MODE_PROCESS, - (int) primary_port->type); + swoole_trace_log(SW_TRACE_SERVER, + "Create Server: host=%s, port=%d, mode=%d, type=%d", + primary_port->host.c_str(), + (int) primary_port->port, + serv->is_base_mode() ? Server::MODE_BASE : Server::MODE_PROCESS, + (int) primary_port->type); #endif if (serv->enable_coroutine) { @@ -1012,13 +855,10 @@ void ServerObject::on_before_start() { } } - /** - * init method - */ - serv->worker_buffer_allocator = sw_zend_string_allocator(); + serv->message_bus.set_allocator(sw_zend_string_allocator()); - if (serv->is_base_mode()) { - serv->buffer_allocator = sw_zend_string_allocator(); + if (serv->is_base_mode() || serv->is_thread_mode()) { + serv->recv_buffer_allocator = sw_zend_string_allocator(); } /** @@ -1040,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()) { @@ -1125,8 +969,8 @@ void ServerObject::on_before_start() { php_swoole_fatal_error( E_ERROR, "server dispatch mode should be FDMOD(%d) or IPMOD(%d) if open_http2_protocol is true", - SW_DISPATCH_FDMOD, - SW_DISPATCH_IPMOD); + Server::DISPATCH_FDMOD, + Server::DISPATCH_IPMOD); return; } @@ -1162,66 +1006,55 @@ void ServerObject::on_before_start() { if (find_http_port) { serv->onReceive = php_swoole_http_server_onReceive; - php_swoole_http_server_init_global_variant(); - } -} - -void ServerObject::register_callback() { - /* - * optional callback - */ - if (property->callbacks[SW_SERVER_CB_onStart] != nullptr) { - serv->onStart = php_swoole_onStart; + if (serv->is_base_mode()) { + serv->onClose = php_swoole_http_server_onClose; + } } - serv->onShutdown = php_swoole_onShutdown; - /** - * require callback, set the master/manager/worker PID - */ - serv->onWorkerStart = php_swoole_server_onWorkerStart; - if (property->callbacks[SW_SERVER_CB_onBeforeReload] != nullptr) { - serv->onBeforeReload = php_swoole_server_onBeforeReload; +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); } +#endif - if (property->callbacks[SW_SERVER_CB_onAfterReload] != nullptr) { - serv->onAfterReload = php_swoole_server_onAfterReload; + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); } +} - if (property->callbacks[SW_SERVER_CB_onWorkerStop] != nullptr) { - serv->onWorkerStop = php_swoole_server_onWorkerStop; - } +void ServerObject::register_callback() { + // control plane + serv->onStart = php_swoole_server_onStart; + serv->onBeforeShutdown = php_swoole_server_onBeforeShutdown; + serv->onShutdown = php_swoole_server_onShutdown; + serv->onWorkerStart = php_swoole_server_onWorkerStart; + serv->onWorkerStop = php_swoole_server_onWorkerStop; serv->onWorkerExit = php_swoole_server_onWorkerExit; - /** - * Task Worker - */ + serv->onBeforeReload = php_swoole_server_onBeforeReload; + serv->onAfterReload = php_swoole_server_onAfterReload; + serv->onManagerStart = php_swoole_server_onManagerStart; + serv->onManagerStop = php_swoole_server_onManagerStop; + serv->onWorkerError = php_swoole_server_onWorkerError; + + // data plane if (property->callbacks[SW_SERVER_CB_onTask] != nullptr) { serv->onTask = php_swoole_server_onTask; serv->onFinish = php_swoole_server_onFinish; } - if (property->callbacks[SW_SERVER_CB_onWorkerError] != nullptr) { - serv->onWorkerError = php_swoole_server_onWorkerError; - } - if (property->callbacks[SW_SERVER_CB_onManagerStart] != nullptr) { - serv->onManagerStart = php_swoole_server_onManagerStart; - } - if (property->callbacks[SW_SERVER_CB_onManagerStop] != nullptr) { - serv->onManagerStop = php_swoole_server_onManagerStop; - } if (property->callbacks[SW_SERVER_CB_onPipeMessage] != nullptr) { - serv->onPipeMessage = php_swoole_onPipeMessage; + serv->onPipeMessage = php_swoole_server_onPipeMessage; } if (serv->send_yield && serv->is_support_unsafe_events()) { serv->onBufferEmpty = php_swoole_server_onBufferEmpty; } } -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) { @@ -1239,28 +1072,28 @@ 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_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; +static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { + 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; } - swTraceLog(SW_TRACE_SERVER, - "PipeMessage: fd=%ld|len=%d|src_worker_id=%d|data=%.*s\n", - req->info.fd, - req->info.len, - req->info.reactor_id, - req->info.len, - req->data); + swoole_trace_log(SW_TRACE_SERVER, + "PipeMessage: fd=%ld|len=%d|src_worker_id=%d|data=%.*s\n", + req->info.fd, + req->info.len, + req->info.reactor_id, + req->info.len, + req->data); zval args[3]; int argc; @@ -1272,33 +1105,35 @@ static void php_swoole_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; @@ -1325,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); } @@ -1340,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; @@ -1415,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)); } @@ -1433,23 +1268,26 @@ 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( - swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("flags"), (zend_long) swTask_type(req)); + swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("flags"), (zend_long) req->info.ext_flags); } 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; } @@ -1460,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, @@ -1480,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); } @@ -1491,42 +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 (swTask_type(req) & SW_TASK_COROUTINE) { - TaskId task_id = req->info.fd; + if (req->info.ext_flags & SW_TASK_COROUTINE) { 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) { - if (task_co->timer) { - swoole_timer_del(task_co->timer); - } - FutureTask *context = &task_co->context; - PHPCoroutine::resume_m(context, zdata); - efree(task_co); - sw_zval_free(zdata); - server_object->property->task_coroutine_map.erase(task_id); + 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; @@ -1534,39 +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 (task_co->timer) { - swoole_timer_del(task_co->timer); - task_co->timer = nullptr; - } - FutureTask *context = &task_co->context; - PHPCoroutine::resume_m(context, result); - sw_zval_free(result); - efree(task_co); + 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; - if (swTask_type(req) & SW_TASK_CALLBACK) { - auto callback_iterator = server_object->property->task_callbacks.find(req->info.fd); + zend::Callable *cb = nullptr; + if (req->info.ext_flags & SW_TASK_CALLBACK) { + auto callback_iterator = server_object->property->task_callbacks.find(task_id); if (callback_iterator == server_object->property->task_callbacks.end()) { - swTask_type(req) = swTask_type(req) & (~SW_TASK_CALLBACK); + 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; } @@ -1578,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 (swTask_type(req) & SW_TASK_CALLBACK) { - sw_zend_fci_cache_discard(fci_cache); - server_object->property->task_callbacks.erase(req->info.fd); + if (req->info.ext_flags & SW_TASK_CALLBACK) { + 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]); } @@ -1608,172 +1427,203 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { return SW_OK; } -static void php_swoole_onStart(Server *serv) { - serv->lock(); - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onStart(Server *serv) { + 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); - if (UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onStart", 1, zserv); + } + + 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); - if (UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onManagerStart", 1, zserv); + } + + 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 (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_onShutdown(Server *serv) { - serv->lock(); - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onBeforeShutdown(Server *serv) { + 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_onBeforeShutdown); - if (fci_cache != nullptr) { - if (UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeShutdown", 1, zserv); + } + + 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)); + } +} + +static void php_swoole_server_onShutdown(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + 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 (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) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { - php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + 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 (fci_cache) { - zval args[1]; - args[0] = *zserv; - if (UNEXPECTED(!zend::function::call(fci_cache, 1, args, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onBeforeReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeReload", 1, zserv); + } + + 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 (fci_cache) { - zval args[1]; - args[0] = *zserv; - if (UNEXPECTED(!zend::function::call(fci_cache, 1, args, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onAfterReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onAfterReload", 1, zserv); + } + + 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 (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); } - if (fci_cache) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { - php_swoole_error(E_WARNING, "%s->onWorkerExit handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onWorkerExit handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } -static void php_swoole_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); +static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { + 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; @@ -1782,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( @@ -1793,14 +1644,18 @@ 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()); argc = 5; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, false))) { + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onWorkerError", 1, zserv); + } + + if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onWorkerError handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1810,62 +1665,61 @@ 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) { - zval *zserv = (zval *) serv->private_data_2; - zval args[3]; - int argc; - args[0] = *zserv; + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onConnect); + if (!cb) { + return; + } - if (serv->event_object) { - zval *object = &args[1]; - object_init_ex(object, swoole_server_event_ce); - zend_update_property_long( - swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) info->fd); - zend_update_property_long( - swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) info->reactor_id); - zend_update_property_double( - swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), info->time); - argc = 2; - } else { - ZVAL_LONG(&args[1], info->fd); - ZVAL_LONG(&args[2], info->reactor_id); - argc = 3; - } + zval *zserv = php_swoole_server_zval_ptr(serv); + zval args[3]; + int argc; + args[0] = *zserv; - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { - php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } + if (serv->event_object) { + zval *object = &args[1]; + object_init_ex(object, swoole_server_event_ce); + zend_update_property_long(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) info->fd); + zend_update_property_long( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) info->reactor_id); + zend_update_property_double( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), info->time); + argc = 2; + } else { + ZVAL_LONG(&args[1], info->fd); + ZVAL_LONG(&args[2], info->reactor_id); + argc = 3; + } - if (serv->event_object) { - zval_ptr_dtor(&args[1]); - } + 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)); + } + + if (serv->event_object) { + zval_ptr_dtor(&args[1]); } } 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; if (serv->enable_coroutine && serv->send_yield) { - auto _i_coros_list = server_object->property->send_coroutine_map.find(session_id); - if (_i_coros_list != server_object->property->send_coroutine_map.end()) { - auto coros_list = _i_coros_list->second; + auto _i_co_list = server_object->property->send_coroutine_map.find(session_id); + if (_i_co_list != server_object->property->send_coroutine_map.end()) { + auto co_list = _i_co_list->second; server_object->property->send_coroutine_map.erase(session_id); - while (!coros_list->empty()) { - FutureTask *context = coros_list->front(); - coros_list->pop_front(); + while (!co_list->empty()) { + Coroutine *co = co_list->front(); + co_list->pop_front(); swoole_set_last_error(ECONNRESET); - zval_ptr_dtor(&context->coro_params); - ZVAL_NULL(&context->coro_params); - php_swoole_server_send_resume(serv, context, session_id); + co->resume(); } - delete coros_list; + delete co_list; } } - 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; @@ -1874,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; @@ -1899,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)); } @@ -1907,138 +1761,78 @@ 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)); } } } -static void php_swoole_server_onSendTimeout(Timer *timer, TimerNode *tnode) { - FutureTask *context = (FutureTask *) tnode->data; - zval *zdata = &context->coro_params; - zval result; - - Server *serv = sw_server(); - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); - - swoole_set_last_error(ETIMEDOUT); - ZVAL_FALSE(&result); - - SessionId session_id = (long) context->private_data; +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(php_swoole_server_zval_ptr(serv))); + Coroutine *co = Coroutine::get_current_safe(); + char *data; + size_t length = php_swoole_get_send_data(zdata, &data); - auto _i_coros_list = server_object->property->send_coroutine_map.find(session_id); - if (_i_coros_list != server_object->property->send_coroutine_map.end()) { - auto coros_list = _i_coros_list->second; - coros_list->remove(context); - // free memory - if (coros_list->size() == 0) { - delete coros_list; - server_object->property->send_coroutine_map.erase(session_id); - } - } else { - swWarn("send coroutine[session#%ld] not exists", session_id); - return; + if (length == 0) { + RETURN_FALSE; } - context->private_data = nullptr; - - PHPCoroutine::resume_m(context, &result); - zval_ptr_dtor(zdata); - efree(context); -} - -static enum swReturn_code php_swoole_server_send_resume(Server *serv, FutureTask *context, SessionId fd) { - char *data; - zval *zdata = &context->coro_params; - zval result; - - if (ZVAL_IS_NULL(zdata)) { - _fail: - ZVAL_FALSE(&result); - } else { - size_t length = php_swoole_get_send_data(zdata, &data); - if (length == 0) { - goto _fail; + SW_LOOP { + auto coroutine_iterator = server_object->property->send_coroutine_map.find(session_id); + std::list *co_list; + if (coroutine_iterator == server_object->property->send_coroutine_map.end()) { + co_list = new std::list; + server_object->property->send_coroutine_map[session_id] = co_list; + } else { + co_list = coroutine_iterator->second; + } + co_list->push_back(co); + auto iter = std::prev(co_list->end()); + if (!co->yield_ex(serv->send_timeout)) { + co_list->erase(iter); + RETURN_FALSE; } - bool ret = serv->send(fd, data, length); + bool ret = serv->send(session_id, data, length); if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD && serv->send_yield) { - return SW_CONTINUE; + continue; + } else { + RETURN_BOOL(ret); } - ZVAL_BOOL(&result, ret); - } - - if (context->timer) { - swoole_timer_del((TimerNode *) context->timer); - context->timer = nullptr; - } - - PHPCoroutine::resume_m(context, &result); - zval_ptr_dtor(zdata); - efree(context); - return SW_READY; -} - -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)); - std::list *coros_list; - auto coroutine_iterator = server_object->property->send_coroutine_map.find(session_id); - - if (coroutine_iterator == server_object->property->send_coroutine_map.end()) { - coros_list = new std::list; - server_object->property->send_coroutine_map[session_id] = coros_list; - } else { - coros_list = coroutine_iterator->second; } - - FutureTask *context = (FutureTask *) emalloc(sizeof(FutureTask)); - coros_list->push_back(context); - context->private_data = (void *) session_id; - if (serv->send_timeout > 0) { - context->timer = - swoole_timer_add((long) (serv->send_timeout * 1000), false, php_swoole_server_onSendTimeout, context); - } else { - context->timer = nullptr; - } - context->coro_params = *zdata; - PHPCoroutine::yield_m(return_value, context); } 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); @@ -2052,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); @@ -2063,64 +1855,86 @@ 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)); - zend_fcall_info_cache *fci_cache; if (serv->send_yield) { - auto _i_coros_list = server_object->property->send_coroutine_map.find(info->fd); - if (_i_coros_list != server_object->property->send_coroutine_map.end()) { - auto coros_list = _i_coros_list->second; + auto _i_co_list = server_object->property->send_coroutine_map.find(info->fd); + if (_i_co_list != server_object->property->send_coroutine_map.end()) { + auto co_list = _i_co_list->second; server_object->property->send_coroutine_map.erase(info->fd); - while (!coros_list->empty()) { - FutureTask *context = coros_list->front(); - coros_list->pop_front(); - if (php_swoole_server_send_resume(serv, context, info->fd) == SW_CONTINUE) { - coros_list->push_back(context); - return; - } + while (!co_list->empty()) { + Coroutine *co = co_list->front(); + co_list->pop_front(); + co->resume(); } - delete coros_list; + delete co_list; } } - 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 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)); +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); } - zval *zserv = ZEND_THIS; - char *host; - size_t host_len = 0; - zend_long sock_type = SW_SOCK_TCP; - zend_long serv_port = 0; - zend_long serv_mode = Server::MODE_PROCESS; + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - // 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)); + 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) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } - if (sw_server() != nullptr) { + zval *zserv = ZEND_THIS; + char *host; + size_t host_len = 0; + zend_long sock_type = SW_SOCK_TCP; + zend_long serv_port = 0; + zend_long serv_mode = Server::MODE_BASE; + + if (!SWOOLE_G(cli)) { zend_throw_exception_ex( - swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); + swoole_exception_ce, -1, "%s can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; } @@ -2132,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 swSocket_type) 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) {} @@ -2199,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)); @@ -2255,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); @@ -2273,42 +2098,23 @@ static PHP_METHOD(swoole_server, set) { if (php_swoole_array_get_value(vht, "send_yield", ztmp)) { serv->send_yield = zval_is_true(ztmp); if (serv->send_yield && - !(serv->dispatch_mode == SW_DISPATCH_FDMOD || serv->dispatch_mode == SW_DISPATCH_IPMOD)) { + !(serv->dispatch_mode == Server::DISPATCH_FDMOD || serv->dispatch_mode == Server::DISPATCH_IPMOD)) { php_swoole_error(E_WARNING, "'send_yield' option can only be set when using dispatch_mode=2/4"); - serv->send_yield = 0; + serv->send_yield = false; } + } else { + 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 @@ -2462,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) { @@ -2471,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 @@ -2491,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 */ @@ -2522,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 */ @@ -2544,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)); } /** @@ -2552,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 @@ -2560,9 +2386,23 @@ 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 == SW_TASK_IPC_MSGQUEUE || serv->task_ipc_mode == SW_TASK_IPC_PREEMPTIVE)) { + (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { php_swoole_fatal_error(E_ERROR, "cannot use msgqueue when task_enable_coroutine is enable"); RETURN_FALSE; } @@ -2578,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; } @@ -2586,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); @@ -2607,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 { @@ -2618,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; @@ -2654,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 swSocket_type) 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; } @@ -2682,56 +2527,127 @@ 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; } if (serv->onUserWorkerStart == nullptr) { - serv->onUserWorkerStart = php_swoole_onUserWorkerStart; + serv->onUserWorkerStart = php_swoole_server_onUserWorkerStart; } 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); + 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"), worker_id); + RETURN_LONG(worker_id); +} - Worker *worker = php_swoole_process_get_and_check_worker(process); - worker->ptr = process; +static PHP_METHOD(swoole_server, addCommand) { + 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 command"); + RETURN_FALSE; + } + + char *name; + size_t l_name; + zend_long accepted_process_types; + zval *zfn; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(name, l_name) + Z_PARAM_LONG(accepted_process_types) + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (accepted_process_types & Server::Command::REACTOR_THREAD) { + php_swoole_fatal_error(E_WARNING, "only support worker/task_worker process"); + RETURN_FALSE; + } + + 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(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}"); + } + + if (!ZVAL_IS_STRING(&return_value)) { + return std::string("{\"data\": \"wrong return type\", \"code\": -2}"); + } + + return std::string(Z_STRVAL(return_value), Z_STRLEN(return_value)); + }; - int id = serv->add_worker(worker); - if (id < 0) { - php_swoole_fatal_error(E_WARNING, "Server::add_worker() failed"); + if (!serv->add_command(std::string(name, l_name), accepted_process_types, fn)) { RETURN_FALSE; } - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), id); - RETURN_LONG(id); + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + server_object->property->command_callbacks.push_back(cb); + RETURN_TRUE; } 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)); @@ -2743,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; } @@ -2788,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; } @@ -2808,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); @@ -2828,7 +2796,7 @@ static PHP_METHOD(swoole_server, sendto) { char *data; size_t len; zend_long server_socket_fd = -1; - enum swSocket_type type; + enum swSocketType type; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(addr, addr_len) @@ -2839,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; } @@ -2898,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); @@ -2937,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); @@ -2956,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); @@ -2976,36 +2952,33 @@ 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); - /** - * reset - */ - int tasking_num = serv->gs->tasking_num; - if (tasking_num < 0) { - tasking_num = serv->gs->tasking_num = 0; - } - - uint32_t idle_worker_num = 0; - uint32_t worker_num = serv->worker_num; - uint32_t task_worker_num = serv->task_worker_num; - - add_assoc_long_ex(return_value, ZEND_STRL("worker_num"), worker_num); - idle_worker_num = serv->get_idle_worker_num(); - - add_assoc_long_ex(return_value, ZEND_STRL("idle_worker_num"), idle_worker_num); - add_assoc_long_ex(return_value, ZEND_STRL("task_worker_num"), task_worker_num); - add_assoc_long_ex(return_value, ZEND_STRL("tasking_num"), tasking_num); - add_assoc_long_ex(return_value, ZEND_STRL("request_count"), serv->gs->request_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_num"), serv->worker_num); + add_assoc_long_ex(return_value, ZEND_STRL("task_worker_num"), serv->task_worker_num); + add_assoc_long_ex(return_value, ZEND_STRL("user_worker_num"), serv->get_user_worker_num()); + add_assoc_long_ex(return_value, ZEND_STRL("idle_worker_num"), serv->get_idle_worker_num()); add_assoc_long_ex(return_value, ZEND_STRL("dispatch_count"), serv->gs->dispatch_count); - - 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_dispatch_count"), SwooleWG.worker->dispatch_count); - } - - if (serv->task_ipc_mode > SW_TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { + add_assoc_long_ex(return_value, ZEND_STRL("request_count"), serv->gs->request_count); + add_assoc_long_ex(return_value, ZEND_STRL("response_count"), serv->gs->response_count); + 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 (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) { size_t queue_num = -1; size_t queue_bytes = -1; if (serv->gs->task_workers.queue->stat(&queue_num, &queue_bytes)) { @@ -3014,12 +2987,14 @@ static PHP_METHOD(swoole_server, stats) { } } - if (task_worker_num > 0) { - idle_worker_num = serv->get_idle_task_worker_num(); - add_assoc_long_ex(return_value, ZEND_STRL("task_idle_worker_num"), idle_worker_num); + 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_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()); + add_assoc_long_ex(return_value, ZEND_STRL("coroutine_peek_num"), Coroutine::get_peak_num()); } static PHP_METHOD(swoole_server, reload) { @@ -3029,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) { @@ -3052,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; @@ -3068,7 +3040,7 @@ static PHP_METHOD(swoole_server, heartbeat) { if (session_id <= 0) { return; } - swTrace("heartbeat check fd=%d", conn->fd); + swoole_trace("heartbeat check fd=%d", conn->fd); if (serv->is_healthy_connection(now, conn)) { return; } @@ -3092,80 +3064,69 @@ 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 = serv->get_task_id(&buf); // coroutine - if (PHPCoroutine::get_cid() >= 0) { - php_swoole_task_wait_co(serv, &buf, timeout, _dst_worker_id, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } + 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); - TaskId task_id = buf.info.fd; + TaskCo task_co{}; + task_co.co = Coroutine::get_current_safe(); + task_co.count = 1; + task_co.result = return_value; - 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; + if (!serv->task(&buf, (int *) &dst_worker_id)) { + RETURN_FALSE; } - } - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); + server_object->property->task_coroutine_map[task_id] = &task_co; + auto retval = task_co.co->yield_ex(timeout); + server_object->property->task_coroutine_map.erase(task_id); - 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; - } + if (!retval) { + RETURN_FALSE; } } else { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); + auto retval = serv->task_sync(&buf, (int *) &dst_worker_id, timeout); + if (!retval) { + RETURN_FALSE; + } + 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); + } } - 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"); @@ -3176,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); @@ -3198,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()) { @@ -3228,17 +3187,20 @@ 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; } - swTask_type(&buf) |= SW_TASK_WAITALL; + 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); @@ -3275,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()); @@ -3314,66 +3272,59 @@ 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; } - TaskCo *task_co = (TaskCo *) emalloc(sizeof(TaskCo)); - if (task_co == nullptr) { - efree(list); - RETURN_FALSE; - } - - task_co->server_object = server_object; + TaskCo task_co; + task_co.co = Coroutine::get_current_safe(); - zval *result = sw_malloc_zval(); - array_init(result); + array_init_size(return_value, n_task); - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) - task_id = php_swoole_task_pack(&buf, ztask); - if (task_id < 0) { - php_swoole_fatal_error(E_WARNING, "failed to pack task"); - goto _fail; - } - swTask_type(&buf) |= (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) { - task_id = -1; - _fail: - add_index_bool(result, i, 0); - n_task--; - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - } else { - server_object->property->task_coroutine_map[task_id] = task_co; + zval *ztask; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), 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; + if (!serv->task(&buf, &dst_worker_id)) { + task_id = -1; + _fail: + add_index_bool(return_value, i, 0); + n_task--; + } else { + server_object->property->task_coroutine_map[task_id] = &task_co; + } + list[i] = task_id; + i++; } - list[i] = task_id; - i++; SW_HASHTABLE_FOREACH_END(); if (n_task == 0) { @@ -3381,17 +3332,21 @@ static PHP_METHOD(swoole_server, taskCo) { RETURN_FALSE; } - long ms = (long) (timeout * 1000); + task_co.result = return_value; + task_co.list = list; + task_co.count = n_task; - task_co->result = result; - task_co->list = list; - task_co->count = n_task; - - TimerNode *timer = swoole_timer_add(ms, false, php_swoole_task_onTimeout, task_co); - if (timer) { - task_co->timer = timer; + 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)) { + if (is_called_in_taskCo) { + add_index_bool(return_value, i, 0); + } + server_object->property->task_coroutine_map.erase(list[i]); + } + } } - PHPCoroutine::yield_m(return_value, &task_co->context); } static PHP_METHOD(swoole_server, task) { @@ -3404,46 +3359,102 @@ 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()) { - swTask_type(&buf) |= SW_TASK_NOREPLY; - } else if (fci.size) { - swTask_type(&buf) |= SW_TASK_CALLBACK; - sw_zend_fci_cache_persist(&fci_cache); - server_object->property->task_callbacks[buf.info.fd] = fci_cache; + buf.info.ext_flags |= SW_TASK_NOREPLY; + } else if (zfn && zval_is_true(zfn)) { + buf.info.ext_flags |= SW_TASK_CALLBACK; + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + server_object->property->task_callbacks[task_id] = cb; } - swTask_type(&buf) |= SW_TASK_NONBLOCK; + 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->task(&buf, (int *) &dst_worker_id)) { + RETURN_LONG(task_id); + } else { + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_server, command) { + char *name; + size_t l_name; + zend_long process_id, process_type; + zval *zdata; + zend_bool json_decode = true; + + 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; + } + + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_STRING(name, l_name) + Z_PARAM_LONG(process_id) + Z_PARAM_LONG(process_type) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(json_decode) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) >= 0) { - RETURN_LONG(buf.info.fd); + smart_str buf = {}; + if (php_json_encode(&buf, zdata, 0) == FAILURE || !buf.s) { + RETURN_FALSE; } - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - RETURN_FALSE; + 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) { + if (php_json_decode(return_value, msg.c_str(), (int) msg.length(), true, 0) == FAILURE) { + RETVAL_FALSE; + } + } else { + ZVAL_STRINGL(return_value, msg.c_str(), msg.length()); + } + + if (co->is_suspending()) { + co->resume(); + } else { + donot_yield = true; + } + }; + + 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(); + } } static PHP_METHOD(swoole_server, sendMessage) { @@ -3460,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; } @@ -3474,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) { @@ -3506,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) { @@ -3523,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; } - swTask_type(&buf) |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); + 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) { @@ -3554,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); @@ -3584,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) { @@ -3611,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) { @@ -3625,10 +3646,10 @@ static PHP_METHOD(swoole_server, getClientInfo) { } else { array_init(return_value); - if (conn->uid > 0 || serv->dispatch_mode == SW_DISPATCH_UIDMOD) { + if (conn->uid > 0 || serv->dispatch_mode == Server::DISPATCH_UIDMOD) { add_assoc_long(return_value, "uid", conn->uid); } - if (conn->worker_id > 0 || serv->dispatch_mode == SW_DISPATCH_CO_CONN_LB) { + if (conn->worker_id > 0 || serv->dispatch_mode == Server::DISPATCH_CO_CONN_LB) { add_assoc_long(return_value, "worker_id", conn->worker_id); } @@ -3644,9 +3665,9 @@ static PHP_METHOD(swoole_server, getClientInfo) { } #endif // server socket - Connection *from_sock = serv->get_connection(conn->server_fd); - if (from_sock) { - add_assoc_long(return_value, "server_port", from_sock->info.get_port()); + Connection *server_socket = serv->get_connection(conn->server_fd); + if (server_socket) { + add_assoc_long(return_value, "server_port", server_socket->info.get_port()); } add_assoc_long(return_value, "server_fd", conn->server_fd); add_assoc_long(return_value, "socket_fd", conn->fd); @@ -3675,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) { @@ -3707,7 +3730,11 @@ static PHP_METHOD(swoole_server, getClientList) { int fd = start_fd + 1; for (; fd <= serv_max_fd; fd++) { - swTrace("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; @@ -3734,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; } @@ -3785,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) { @@ -3803,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()); } } @@ -3815,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 { @@ -3835,43 +3862,34 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { static PHP_METHOD(swoole_server, getWorkerPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (!serv->is_worker() && !serv->is_task_worker()) { - zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &worker_id) == FAILURE) { - RETURN_FALSE; - } - if (worker_id < 0) { - RETURN_FALSE; - } - RETURN_LONG(serv->get_worker(worker_id)->pid); - } else { - RETURN_LONG(SwooleG.pid); + zend_long worker_id = -1; + + 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; } + RETURN_LONG(worker->pid); } 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. swKill(%d, SIGTERM) failed", serv->gs->master_pid); - RETURN_FALSE; - } else { - RETURN_TRUE; - } + RETURN_BOOL(serv->shutdown()); } static PHP_METHOD(swoole_server, stop) { @@ -3882,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; } @@ -3964,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->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 d657150ac9e..9e5fdadb0a8 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -10,47 +10,53 @@ | 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; -struct server_port_event { +struct ServerPortEvent { enum php_swoole_server_port_callback_type type; std::string name; - server_port_event(enum php_swoole_server_port_callback_type type, std::string &&name) : type(type), name(name) {} + ServerPortEvent(enum php_swoole_server_port_callback_type type, std::string &&name) : type(type), name(name) {} }; // clang-format off -static std::unordered_map server_port_event_map({ - { "connect", server_port_event(SW_SERVER_CB_onConnect, "Connect") }, - { "receive", server_port_event(SW_SERVER_CB_onReceive, "Receive") }, - { "close", server_port_event(SW_SERVER_CB_onClose, "Close") }, - { "packet", server_port_event(SW_SERVER_CB_onPacket, "Packet") }, - { "bufferfull", server_port_event(SW_SERVER_CB_onBufferFull, "BufferFull") }, - { "bufferempty", server_port_event(SW_SERVER_CB_onBufferEmpty, "BufferEmpty") }, - { "request", server_port_event(SW_SERVER_CB_onRequest, "Request") }, - { "handshake", server_port_event(SW_SERVER_CB_onHandShake, "Handshake") }, - { "open", server_port_event(SW_SERVER_CB_onOpen, "Open") }, - { "message", server_port_event(SW_SERVER_CB_onMessage, "Message") }, - { "disconnect", server_port_event(SW_SERVER_CB_onDisconnect, "Disconnect") }, +static std::unordered_map server_port_event_map({ + { "connect", ServerPortEvent(SW_SERVER_CB_onConnect, "Connect") }, + { "receive", ServerPortEvent(SW_SERVER_CB_onReceive, "Receive") }, + { "close", ServerPortEvent(SW_SERVER_CB_onClose, "Close") }, + { "packet", ServerPortEvent(SW_SERVER_CB_onPacket, "Packet") }, + { "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") }, + { "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") }, }); // clang-format on zend_class_entry *swoole_server_port_ce; static zend_object_handlers swoole_server_port_handlers; -struct server_port_t { +struct ServerPortObject { ListenPort *port; ServerPortProperty property; zend_object std; }; -static sw_inline server_port_t *php_swoole_server_port_fetch_object(zend_object *obj) { - return (server_port_t *) ((char *) obj - swoole_server_port_handlers.offset); +static sw_inline ServerPortObject *php_swoole_server_port_fetch_object(zend_object *obj) { + return (ServerPortObject *) ((char *) obj - swoole_server_port_handlers.offset); } static sw_inline ListenPort *php_swoole_server_port_get_ptr(zval *zobject) { @@ -83,13 +89,13 @@ static ServerPortProperty *php_swoole_server_port_get_and_check_property(zval *z // Dereference from server object void php_swoole_server_port_deref(zend_object *object) { - server_port_t *server_port = php_swoole_server_port_fetch_object(object); + ServerPortObject *server_port = php_swoole_server_port_fetch_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; } @@ -112,7 +117,7 @@ static void php_swoole_server_port_free_object(zend_object *object) { } static zend_object *php_swoole_server_port_create_object(zend_class_entry *ce) { - server_port_t *server_port = (server_port_t *) zend_object_alloc(sizeof(server_port_t), ce); + ServerPortObject *server_port = (ServerPortObject *) zend_object_alloc(sizeof(ServerPortObject), ce); zend_object_std_init(&server_port->std, ce); object_properties_init(&server_port->std, ce); server_port->std.handlers = &swoole_server_port_handlers; @@ -132,47 +137,31 @@ 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_SET_CLASS_SERIALIZABLE(swoole_server_port, zend_class_serialize_deny, zend_class_unserialize_deny); + 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, php_swoole_server_port_create_object, php_swoole_server_port_free_object, - server_port_t, + ServerPortObject, std); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onConnect"), ZEND_ACC_PRIVATE); @@ -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 4054781e801..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,69 +713,75 @@ 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_SET_CLASS_SERIALIZABLE(swoole_socket_coro, zend_class_serialize_deny, zend_class_unserialize_deny); + 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()); - zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("domain"), sock->socket->get_sock_domain()); - zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("type"), sock->socket->get_sock_type()); - zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("protocol"), sock->socket->get_sock_protocol()); + zend_update_property_long( + swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("domain"), sock->socket->get_sock_domain()); + zend_update_property_long( + swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("type"), sock->socket->get_sock_type()); + zend_update_property_long( + swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("protocol"), sock->socket->get_sock_protocol()); } 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; } -SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocket_type type) { +SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type) { php_swoole_check_reactor(); int new_fd = dup(fd); if (new_fd < 0) { @@ -873,12 +791,12 @@ SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocket_type type) { return php_swoole_create_socket_from_fd(new_fd, type); } -SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocket_type 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; @@ -888,22 +806,68 @@ SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocket_type } 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) { @@ -962,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; @@ -980,7 +944,7 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { sock->open_length_check = zval_is_true(ztmp); if (zval_is_true(ztmp)) { - swMqtt_set_protocol(&sock->protocol); + swoole::mqtt::set_protocol(&sock->protocol); } } // open length check @@ -1012,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; @@ -1054,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]; @@ -1065,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) { @@ -1103,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) { @@ -1133,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; @@ -1150,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; @@ -1168,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; } } @@ -1200,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; @@ -1211,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; @@ -1221,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; @@ -1241,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; @@ -1260,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; @@ -1296,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; @@ -1309,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) { @@ -1334,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) { @@ -1351,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; @@ -1366,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 { @@ -1375,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; @@ -1395,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; } @@ -1440,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; @@ -1465,40 +1626,41 @@ 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; } std::unique_ptr iov(new iovec[iovcnt]); - SW_HASHTABLE_FOREACH_START(vht, zelement) - if (!ZVAL_IS_LONG(zelement)) { - zend_throw_exception_ex(swoole_socket_coro_exception_ce, - EINVAL, - "Item #[%d] must be of type int, %s given", - iov_index, - zend_get_type_by_const(Z_TYPE_P(zelement))); - RETURN_FALSE; - } - if (Z_LVAL_P(zelement) < 0) { - zend_throw_exception_ex( - swoole_socket_coro_exception_ce, EINVAL, "Item #[%d] must be greater than 0", iov_index); - RETURN_FALSE; - } - size_t iov_len = Z_LVAL_P(zelement); + SW_HASHTABLE_FOREACH_START(vht, zelement) { + if (!ZVAL_IS_LONG(zelement)) { + zend_throw_exception_ex(swoole_socket_coro_exception_ce, + EINVAL, + "Item #[%d] must be of type int, %s given", + iov_index, + zend_get_type_by_const(Z_TYPE_P(zelement))); + RETURN_FALSE; + } + if (Z_LVAL_P(zelement) < 0) { + zend_throw_exception_ex( + swoole_socket_coro_exception_ce, EINVAL, "Item #[%d] must be greater than 0", iov_index); + RETURN_FALSE; + } + size_t iov_len = Z_LVAL_P(zelement); - iov[iov_index].iov_base = zend_string_alloc(iov_len, 0)->val; - iov[iov_index].iov_len = iov_len; - iov_index++; - total_length += iov_len; + iov[iov_index].iov_base = zend_string_alloc(iov_len, 0)->val; + iov[iov_index].iov_len = iov_len; + iov_index++; + total_length += iov_len; + } SW_HASHTABLE_FOREACH_END(); swoole::network::IOVector io_vector((struct iovec *) iov.get(), iovcnt); @@ -1533,6 +1695,7 @@ static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const b real_count = iov_index + 1; zend_string *str = zend::fetch_zend_string_by_val((char *) iov[iov_index].iov_base); iov[iov_index].iov_base = sw_zend_string_recycle(str, iov[iov_index].iov_len, offset_bytes)->val; + iov[iov_index].iov_len = offset_bytes; free_func(iov.get(), iovcnt, real_count); } else { real_count = iovcnt; @@ -1546,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) { @@ -1559,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; @@ -1580,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) { @@ -1598,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; @@ -1633,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 { @@ -1652,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; @@ -1661,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; } @@ -1680,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; } @@ -1694,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; } @@ -1709,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(); @@ -1767,13 +1946,30 @@ static PHP_METHOD(swoole_socket_coro, getOption) { } case SO_RCVTIMEO: case SO_SNDTIMEO: { - double timeout = sock->socket->get_timeout(optname == SO_RCVTIMEO ? Socket::TIMEOUT_READ : Socket::TIMEOUT_WRITE); + double timeout = + sock->socket->get_timeout(optname == SO_RCVTIMEO ? Socket::TIMEOUT_READ : Socket::TIMEOUT_WRITE); array_init(return_value); - int sec = (int) timeout; + int sec = (int) timeout; add_assoc_long(return_value, "sec", (int) timeout); 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); @@ -1808,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); @@ -1956,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 eb3949594cb..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); @@ -181,6 +135,7 @@ static PHP_METHOD(swoole_table, count); static PHP_METHOD(swoole_table, destroy); static PHP_METHOD(swoole_table, getSize); static PHP_METHOD(swoole_table, getMemorySize); +static PHP_METHOD(swoole_table, stats); static PHP_METHOD(swoole_table, rewind); static PHP_METHOD(swoole_table, next); @@ -193,34 +148,35 @@ 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, __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_SET_CLASS_SERIALIZABLE(swoole_table, zend_class_serialize_deny, zend_class_unserialize_deny); + 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); SW_SET_CLASS_CUSTOM_OBJECT( @@ -241,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; @@ -296,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; } @@ -344,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); @@ -596,6 +554,22 @@ static PHP_METHOD(swoole_table, getSize) { } } +static PHP_METHOD(swoole_table, stats) { + Table *table = php_swoole_table_get_ptr(ZEND_THIS); + if (!table) { + RETURN_FALSE; + } + array_init(return_value); + add_assoc_long(return_value, "num", table->count()); + add_assoc_long(return_value, "conflict_count", table->conflict_count); + add_assoc_long(return_value, "conflict_max_level", table->conflict_max_level); + add_assoc_long(return_value, "insert_count", table->insert_count); + add_assoc_long(return_value, "update_count", table->update_count); + add_assoc_long(return_value, "delete_count", table->delete_count); + add_assoc_long(return_value, "available_slice_num", table->get_available_slice_num()); + add_assoc_long(return_value, "total_slice_num", table->get_total_slice_num()); +} + static PHP_METHOD(swoole_table, rewind) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); table->rewind(); 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 c336e32b33f..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); } @@ -269,6 +251,7 @@ static PHP_FUNCTION(swoole_timer_info) { } array_init(return_value); add_assoc_long(return_value, "exec_msec", tnode->exec_msec); + add_assoc_long(return_value, "exec_count", tnode->exec_count); add_assoc_long(return_value, "interval", tnode->interval); add_assoc_long(return_value, "round", tnode->round); add_assoc_bool(return_value, "removed", tnode->removed); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 55435e9334f..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" @@ -30,8 +31,6 @@ using swoole::SessionId; using swoole::String; using swoole::coroutine::Socket; -using http_request = swoole::http::Request; -using http_response = swoole::http::Response; using HttpContext = swoole::http::Context; namespace WebSocket = swoole::websocket; @@ -45,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); @@ -56,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 @@ -153,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 @@ -209,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( @@ -242,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) { @@ -256,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 { @@ -275,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); } @@ -351,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; @@ -362,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; @@ -395,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; @@ -405,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; @@ -429,7 +393,7 @@ static bool websocket_message_uncompress(String *buffer, const char *in, size_t // gzip_stream.total_out = 0; status = inflateInit2(&zstream, SW_ZLIB_ENCODING_RAW); if (status != Z_OK) { - swWarn("inflateInit2() failed by %s", zError(status)); + swoole_warning("inflateInit2() failed by %s", zError(status)); return false; } @@ -461,7 +425,7 @@ static bool websocket_message_uncompress(String *buffer, const char *in, size_t inflateEnd(&zstream); if (!ret) { - swWarn("inflate() failed, Error: %s[%d]", zError(status), status); + swoole_warning("inflate() failed, Error: %s[%d]", zError(status), status); return false; } return true; @@ -483,7 +447,7 @@ static bool websocket_message_compress(String *buffer, const char *data, size_t status = deflateInit2(&zstream, level, Z_DEFLATED, SW_ZLIB_ENCODING_RAW, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (status != Z_OK) { - swWarn("deflateInit2() failed, Error: [%d]", status); + swoole_warning("deflateInit2() failed, Error: [%d]", status); return false; } @@ -526,12 +490,12 @@ static bool websocket_message_compress(String *buffer, const char *data, size_t deflateEnd(&zstream); if (result != Z_BUF_ERROR || bytes_written < 4) { - swWarn("Failed to compress outgoing frame"); + swoole_warning("Failed to compress outgoing frame"); return false; } if (status != Z_OK) { - swWarn("deflate() failed, Error: [%d]", status); + swoole_warning("deflate() failed, Error: [%d]", status); return false; } @@ -580,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); } @@ -624,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); - SW_SET_CLASS_SERIALIZABLE(swoole_websocket_server, zend_class_serialize_deny, zend_class_unserialize_deny); +#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( @@ -687,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 }}} */ @@ -716,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); @@ -742,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; } @@ -774,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) { @@ -815,34 +801,56 @@ static PHP_METHOD(swoole_websocket_server, push) { #ifdef SW_HAVE_ZLIB 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#" 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; } } 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; @@ -859,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) { @@ -885,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); } @@ -901,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 8c8727d355b..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; @@ -107,6 +110,18 @@ typedef unsigned long ulong_t; #define SW_ECHO_MAGENTA "\e[35m%s\e[0m" #define SW_ECHO_CYAN "\e[36m%s\e[0m" #define SW_ECHO_WHITE "\e[37m%s\e[0m" + +#define SW_ECHO_LEN_RED "\e[31m%.*s\e[0m" +#define SW_ECHO_LEN_GREEN "\e[32m%.*s\e[0m" +#define SW_ECHO_LEN_YELLOW "\e[33m%.*s\e[0m" +#define SW_ECHO_LEN_BLUE "\e[34m%.*s\e[0m" +#define SW_ECHO_LEN_MAGENTA "\e[35m%.*s\e[0m" +#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 @@ -140,8 +155,8 @@ typedef unsigned long ulong_t; #define MIN(A, B) SW_MIN(A, B) #endif -#define SW_NUM_BILLION (1000 * 1000 * 1000) -#define SW_NUM_MILLION (1000 * 1000) +#define SW_NUM_BILLION (1000 * 1000 * 1000) +#define SW_NUM_MILLION (1000 * 1000) #ifdef SW_DEBUG #define SW_ASSERT(e) assert(e) @@ -157,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); @@ -181,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; } @@ -195,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; @@ -205,8 +239,6 @@ typedef swoole::Protocol swProtocol; typedef swoole::EventData swEventData; typedef swoole::DataHead swDataHead; typedef swoole::Event swEvent; -typedef swoole::Pipe swPipe; -typedef swoole::Callback swCallback; /*----------------------------------String-------------------------------------*/ @@ -216,9 +248,8 @@ typedef swoole::Callback swCallback; #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 @@ -229,8 +260,7 @@ typedef swoole::Callback swCallback; #endif /** always return less than size, zero termination */ -size_t sw_snprintf(char *buf, size_t size, const char *format, ...) - __attribute__ ((format (printf, 3, 4))); +size_t sw_snprintf(char *buf, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4))); size_t sw_vsnprintf(char *buf, size_t size, const char *format, va_list args); #define sw_memset_zero(s, n) memset(s, '\0', n) @@ -275,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); } @@ -302,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, @@ -342,21 +391,23 @@ static inline void swoole_strtolower(char *str, int length) { } /*--------------------------------Constants------------------------------------*/ -enum swResult_code { - SW_OK = 0, +enum swResultCode { + SW_OK = 0, SW_ERR = -1, }; -enum swReturn_code { +enum swReturnCode { + SW_SUCCESS = 0, SW_CONTINUE = 1, - SW_WAIT = 2, - SW_CLOSE = 3, - SW_ERROR = 4, - SW_READY = 5, - SW_INVALID = 6, + SW_WAIT = 2, + SW_CLOSE = 3, + SW_ERROR = 4, + SW_READY = 5, + SW_INVALID = 6, + SW_REDUCE_SIZE = 7, }; -enum swFd_type { +enum swFdType { SW_FD_SESSION, // server stream session SW_FD_STREAM_SERVER, // server stream port SW_FD_DGRAM_SERVER, // server dgram port @@ -366,21 +417,25 @@ enum swFd_type { /** * Coroutine Socket */ - SW_FD_CORO_SOCKET, + SW_FD_CO_SOCKET, /** * socket poll fd [coroutine::socket_poll] */ - SW_FD_CORO_POLL, + SW_FD_CO_POLL, /** * event waiter */ - SW_FD_CORO_EVENT, + SW_FD_CO_EVENT, /** * signalfd */ 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 */ @@ -389,39 +444,45 @@ enum swFd_type { SW_FD_DGRAM_CLIENT, }; -enum swSocket_flag { +enum swSocketFlag { SW_SOCK_NONBLOCK = 1 << 2, - SW_SOCK_CLOEXEC = 1 << 3, - SW_SOCK_SSL = (1u << 9), + SW_SOCK_CLOEXEC = 1 << 3, + SW_SOCK_SSL = (1u << 9), }; -enum swSocket_type { - SW_SOCK_TCP = 1, - SW_SOCK_UDP = 2, - SW_SOCK_TCP6 = 3, - SW_SOCK_UDP6 = 4, +enum swSocketType { + SW_SOCK_TCP = 1, + SW_SOCK_UDP = 2, + SW_SOCK_TCP6 = 3, + SW_SOCK_UDP6 = 4, SW_SOCK_UNIX_STREAM = 5, // unix sock stream - SW_SOCK_UNIX_DGRAM = 6, // unix sock dgram - SW_SOCK_RAW = 7, + SW_SOCK_UNIX_DGRAM = 6, // unix sock dgram + SW_SOCK_RAW = 7, }; -enum swEvent_type { - SW_EVENT_NULL = 0, +enum swEventType { + SW_EVENT_NULL = 0, SW_EVENT_DEAULT = 1u << 8, - SW_EVENT_READ = 1u << 9, - SW_EVENT_WRITE = 1u << 10, - SW_EVENT_RDWR = SW_EVENT_READ | SW_EVENT_WRITE, - SW_EVENT_ERROR = 1u << 11, - SW_EVENT_ONCE = 1u << 12, + SW_EVENT_READ = 1u << 9, + SW_EVENT_WRITE = 1u << 10, + SW_EVENT_RDWR = SW_EVENT_READ | SW_EVENT_WRITE, + SW_EVENT_ERROR = 1u << 11, + SW_EVENT_ONCE = 1u << 12, }; -enum swFork_type { - SW_FORK_SPAWN = 0, - SW_FORK_EXEC = 1 << 1, - SW_FORK_DAEMON = 1 << 2, +enum swForkType { + SW_FORK_SPAWN = 0, + SW_FORK_EXEC = 1 << 1, + SW_FORK_DAEMON = 1 << 2, SW_FORK_PRECHECK = 1 << 3, }; +enum swTraverseOperation { + SW_TRAVERSE_KEEP = 0, + SW_TRAVERSE_REMOVE = 1, + SW_TRAVERSE_STOP = 2, +}; + //------------------------------------------------------------------------------- #define sw_yield() sched_yield() @@ -434,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) { @@ -451,56 +513,47 @@ static sw_inline size_t swoole_size_align(size_t size, int pagesize) { } //------------------------------Base-------------------------------- -enum swEventData_flag { +enum swEventDataFlag { SW_EVENT_DATA_NORMAL, - SW_EVENT_DATA_PTR = 1u << 1, - SW_EVENT_DATA_CHUNK = 1u << 2, - SW_EVENT_DATA_BEGIN = 1u << 3, - SW_EVENT_DATA_END = 1u << 4, + SW_EVENT_DATA_PTR = 1u << 1, + SW_EVENT_DATA_CHUNK = 1u << 2, + SW_EVENT_DATA_BEGIN = 1u << 3, + SW_EVENT_DATA_END = 1u << 4, SW_EVENT_DATA_OBJ_PTR = 1u << 5, SW_EVENT_DATA_POP_PTR = 1u << 6, }; -#define swTask_type(task) ((task)->info.server_fd) - -/** - * use swDataHead->server_fd, 1 byte 8 bit - */ -enum swTask_type { - SW_TASK_TMPFILE = 1, // tmp file - SW_TASK_SERIALIZE = 2, // php serialize - SW_TASK_NONBLOCK = 4, // task - SW_TASK_CALLBACK = 8, // callback - SW_TASK_WAITALL = 16, // for taskWaitAll - SW_TASK_COROUTINE = 32, // coroutine - SW_TASK_PEEK = 64, // peek - SW_TASK_NOREPLY = 128, // don't reply +enum swTaskFlag { + SW_TASK_TMPFILE = 1, + SW_TASK_SERIALIZE = 1u << 1, + SW_TASK_NONBLOCK = 1u << 2, + SW_TASK_CALLBACK = 1u << 3, + SW_TASK_WAITALL = 1u << 4, + SW_TASK_COROUTINE = 1u << 5, + SW_TASK_PEEK = 1u << 6, + SW_TASK_NOREPLY = 1u << 7, }; -enum swDNSLookup_cache_type { +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 swProcess_type { - SW_PROCESS_MASTER = 1, - SW_PROCESS_WORKER = 2, - SW_PROCESS_MANAGER = 3, +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, }; -enum swPipe_type { - SW_PIPE_WORKER = 0, - SW_PIPE_MASTER = 1, - SW_PIPE_READ = 0, - SW_PIPE_WRITE = 1, +enum swPipeType { + SW_PIPE_WORKER = 0, + SW_PIPE_MASTER = 1, + SW_PIPE_READ = 0, + SW_PIPE_WRITE = 1, SW_PIPE_NONBLOCK = 2, }; @@ -522,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); @@ -537,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); @@ -565,17 +627,24 @@ static inline struct timespec swoole_time_until(int milliseconds) { } namespace swoole { + +typedef long SessionId; +typedef long TaskId; +typedef uint8_t ReactorId; +typedef uint32_t WorkerId; +typedef enum swEventType EventType; +typedef enum swSocketType SocketType; +typedef enum swFdType FdType; +typedef enum swReturnCode ReturnCode; +typedef enum swResultCode ResultCode; + struct Event { int fd; int16_t reactor_id; - enum swFd_type type; + FdType type; network::Socket *socket; }; -typedef long SessionId; -typedef long TaskId; -typedef uint8_t ReactorId; - struct DataHead { SessionId fd; uint64_t msg_id; @@ -585,25 +654,51 @@ struct DataHead { uint8_t flags; uint16_t server_fd; uint16_t ext_flags; + uint32_t reserved; double time; size_t dump(char *buf, size_t len); + void print(); }; struct EventData { DataHead info; char data[SW_IPC_BUFFER_SIZE]; - bool pack(const void *data, size_t data_len); - bool unpack(String *buffer); + + 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; @@ -616,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; @@ -653,15 +775,24 @@ struct Global { double dns_cache_refresh_time; 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; //-----------------------[Hook]-------------------------- void *hooks[SW_MAX_HOOK_TYPE]; - std::function user_exit_condition; + std::function user_exit_condition; + // bug report message + std::string bug_report_message; }; std::string dirname(const std::string &file); @@ -671,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) @@ -679,29 +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 int swoole_get_process_id() { +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 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_dns_server(const std::string server); +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 151c9c23590..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 | +----------------------------------------------------------------------+ */ @@ -21,13 +21,19 @@ #include "swoole.h" #include "swoole_coroutine_c_api.h" -enum swEvent_init_flags { +enum swEventInitFlag { SW_EVENTLOOP_WAIT_EXIT = 1, }; 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); @@ -46,11 +52,13 @@ SW_API int swoole_event_del(swoole::network::Socket *socket); SW_API void swoole_event_defer(swoole::Callback cb, void *private_data); SW_API ssize_t swoole_event_write(swoole::network::Socket *socket, const void *data, size_t len); SW_API ssize_t swoole_event_writev(swoole::network::Socket *socket, const iovec *iov, size_t iovcnt); +SW_API swoole::network::Socket *swoole_event_get_socket(int fd); SW_API int swoole_event_wait(); 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 f1ef612ac6b..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 @@ -29,29 +27,27 @@ namespace swoole { -enum AsyncFlags { +enum AsyncFlag { SW_AIO_WRITE_FSYNC = 1u << 1, 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,24 +62,67 @@ 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; - uint32_t task_num = 0; + 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; AsyncThreads(); ~AsyncThreads(); - size_t thread_count(); + size_t get_task_num() { + return task_num; + } + + size_t get_queue_size(); + size_t get_worker_num(); 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 a711b631d8b..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 | +----------------------------------------------------------------------+ */ @@ -22,8 +22,11 @@ extern "C" { #endif #include +#include -enum swGlobal_hook_type { +#include "swoole_config.h" + +enum swGlobalHookType { SW_GLOBAL_HOOK_BEFORE_SERVER_START, SW_GLOBAL_HOOK_BEFORE_CLIENT_START, SW_GLOBAL_HOOK_BEFORE_WORKER_START, @@ -31,17 +34,22 @@ enum swGlobal_hook_type { SW_GLOBAL_HOOK_ON_CORO_STOP, SW_GLOBAL_HOOK_ON_REACTOR_CREATE, SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, + SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN, SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, - SW_GLOBAL_HOOK_END = SW_MAX_HOOK_TYPE - 1, + SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, + SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, + 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 swGlobal_hook_type type, swHookFunc cb, int push_back); -void swoole_call_hook(enum swGlobal_hook_type type, void *arg); +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); + const char *swoole_version(void); int swoole_version_id(void); int swoole_api_version_id(void); 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 195b8a2bee6..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 { @@ -35,7 +33,7 @@ class Client { int _sock_type = 0; int _sock_domain = 0; int _protocol = 0; - enum swFd_type fd_type; + FdType fd_type; bool active = false; bool async = false; bool keep = false; @@ -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; @@ -118,7 +116,7 @@ class Client { ssize_t (*recv)(Client *cli, char *data, size_t length, int flags) = nullptr; static void init_reactor(Reactor *reactor); - Client(enum swSocket_type type, bool async); + Client(SocketType type, bool async); ~Client(); void set_http_proxy(const std::string &host, int port) { @@ -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, enum swSocket_type 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; @@ -178,7 +189,7 @@ class Stream { static void set_protocol(Protocol *protocol); private: - Stream(const char *dst_host, int dst_port, enum swSocket_type type); + Stream(const char *dst_host, int dst_port, SocketType type); }; //----------------------------------------Stream End------------------------------------ @@ -188,10 +199,10 @@ class SyncClient { bool connected = false; bool created; bool async = false; - enum swSocket_type type; + SocketType type; public: - SyncClient(enum swSocket_type _type, bool _async = false) : client(_type, _async), async(_async), type(_type) { + SyncClient(SocketType _type, bool _async = false) : client(_type, _async), async(_async), type(_type) { created = client.socket != nullptr; } @@ -252,7 +263,7 @@ class AsyncClient : public SyncClient { std::function _onReceive = nullptr; public: - AsyncClient(enum swSocket_type _type) : SyncClient(_type, true) {} + AsyncClient(SocketType _type) : SyncClient(_type, true) {} bool connect(const char *host, int port, double timeout = -1) { client.object = this; diff --git a/include/swoole_config.h b/include/swoole_config.h index fd6988dca45..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,7 +74,8 @@ #else #define SW_IPC_MAX_SIZE 8192 // for IPC, dgram and message-queue max size #endif -#define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swDataHead)) +#define SW_IPC_BUFFER_MAX_SIZE (64 * 1024) +#define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swoole::DataHead)) // !!!End.------------------------------------------------------------------- #define SW_BUFFER_SIZE_STD 8192 @@ -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,27 +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_SEND_TWICE 1 +#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" @@ -271,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 804eb7b4121..ff19aff2ad2 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,15 +23,24 @@ #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 +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 { public: @@ -60,43 +69,41 @@ 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(); void yield(CancelFunc *cancel_fn); bool cancel(); - void resume_naked(); - void yield_naked(); 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(); } @@ -112,7 +119,7 @@ class Coroutine { return state == STATE_WAITING; } - inline void set_task(void *_task) { + void set_task(void *_task) { task = _task; } @@ -120,20 +127,35 @@ 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()); - swWarn("failed to create coroutine, Error: %s[%d]", e.what(), swoole_get_last_error()); + swoole_warning("failed to create coroutine, Error: %s[%d]", e.what(), swoole_get_last_error()); return -1; } #else @@ -150,7 +172,7 @@ class Coroutine { static inline Coroutine *get_current_safe() { if (sw_unlikely(!current)) { - swFatalError(SW_ERROR_CO_OUT_OF_COROUTINE, "API must be called in the coroutine"); + swoole_fatal_error(SW_ERROR_CO_OUT_OF_COROUTINE, "API must be called in the coroutine"); } return current; } @@ -198,53 +220,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(); + } } - inline long run() { + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { + cid = _cid; + } + + 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); } } @@ -252,9 +299,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 ec860867bae..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 | +----------------------------------------------------------------------+ */ @@ -32,7 +32,7 @@ namespace coroutine { //------------------------------------------------------------------------------- class Channel { public: - enum opcode { + enum Opcode { PRODUCER = 1, CONSUMER = 2, }; @@ -46,7 +46,7 @@ class Channel { struct TimeoutMessage { Channel *chan; - enum opcode type; + Opcode type; Coroutine *co; bool error; TimerNode *timer; @@ -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; } @@ -120,27 +120,27 @@ class Channel { static void timer_callback(Timer *timer, TimerNode *tnode); - void yield(enum opcode type); + 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(); producer_queue.pop_front(); - swTraceLog(SW_TRACE_CHANNEL, "resume producer cid=%ld", co->get_cid()); + swoole_trace_log(SW_TRACE_CHANNEL, "resume producer cid=%ld", co->get_cid()); } else // if (type == CONSUMER) { co = consumer_queue.front(); consumer_queue.pop_front(); - swTraceLog(SW_TRACE_CHANNEL, "resume consumer cid=%ld", co->get_cid()); + swoole_trace_log(SW_TRACE_CHANNEL, "resume consumer cid=%ld", co->get_cid()); } return co; } 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 e02236d2415..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 | +----------------------------------------------------------------------+ */ @@ -62,17 +62,25 @@ class Socket { Socket(int domain, int type, int protocol); Socket(int _fd, int _domain, int _type, int _protocol); - Socket(enum swSocket_type type = SW_SOCK_TCP); - Socket(int _fd, enum swSocket_type _type); + 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 enum swEvent_type event); + 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(enum swEvent_type 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,66 +147,65 @@ 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(); #endif static inline void init_reactor(Reactor *reactor) { - reactor->set_handler(SW_FD_CORO_SOCKET | SW_EVENT_READ, readable_event_callback); - reactor->set_handler(SW_FD_CORO_SOCKET | SW_EVENT_WRITE, writable_event_callback); - reactor->set_handler(SW_FD_CORO_SOCKET | SW_EVENT_ERROR, error_event_callback); + reactor->set_handler(SW_FD_CO_SOCKET | SW_EVENT_READ, readable_event_callback); + reactor->set_handler(SW_FD_CO_SOCKET | SW_EVENT_WRITE, writable_event_callback); + reactor->set_handler(SW_FD_CO_SOCKET | SW_EVENT_ERROR, error_event_callback); } - inline enum swSocket_type get_type() { + SocketType get_type() { return type; } - inline enum swFd_type 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 enum swEvent_type 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 enum swEvent_type event) { + Coroutine *get_bound_co(const EventType event) { if (event & SW_EVENT_READ) { if (read_co) { return read_co; @@ -213,12 +219,12 @@ class Socket { return nullptr; } - inline long get_bound_cid(const enum swEvent_type 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; } - const char *get_event_str(const enum swEvent_type event) { + const char *get_event_str(const EventType event) { if (event == SW_EVENT_READ) { return "reading"; } else if (event == SW_EVENT_WRITE) { @@ -228,32 +234,32 @@ class Socket { } } - inline void check_bound_co(const enum swEvent_type event) { + void check_bound_co(const EventType event) { long cid = get_bound_cid(event); if (sw_unlikely(cid)) { - swFatalError(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()); + 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()); } } - 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,15 +305,15 @@ 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) { - swSysWarn("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); + swoole_sys_warning("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); return false; } 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,23 +359,34 @@ 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; } buffer_init_size = size; } + int move_fd() { + sock_fd = SW_BAD_SOCKET; + return socket->move_fd(); + } + + network::Socket *move_socket() { + network::Socket *_socket = socket; + socket = nullptr; + return _socket; + } + #ifdef SW_USE_OPENSSL - inline bool ssl_is_available() { + bool ssl_is_available() { return socket && ssl_handshaked; } @@ -373,7 +398,7 @@ class Socket { #endif private: - enum swSocket_type type; + SocketType type; network::Socket *socket = nullptr; int sock_domain = 0; int sock_type = 0; @@ -383,7 +408,7 @@ class Socket { Coroutine *read_co = nullptr; Coroutine *write_co = nullptr; #ifdef SW_USE_OPENSSL - enum swEvent_type want_event = SW_EVENT_NULL; + EventType want_event = SW_EVENT_NULL; #endif std::string connect_host; @@ -414,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); @@ -431,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(enum swSocket_type _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) { @@ -443,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); } @@ -454,18 +482,23 @@ class Socket { protocol.package_max_length = SW_INPUT_BUFFER_SIZE; } - bool add_event(const enum swEvent_type event); - bool wait_event(const enum swEvent_type event, const void **__buf = nullptr, size_t __n = 0); + 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 enum swEvent_type 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; @@ -476,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; @@ -509,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]); } } } @@ -543,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(); @@ -558,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; @@ -575,6 +609,7 @@ class ProtocolSwitch { bool ori_open_length_check; Protocol ori_protocol; Socket *socket_; + public: ProtocolSwitch(Socket *socket) { ori_open_eof_check = socket->open_eof_check; @@ -596,8 +631,9 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam #ifdef SW_USE_CARES std::vector dns_lookup_impl_with_cares(const char *domain, int family, double timeout); #endif +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 251ef3f4ba2..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,11 +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 @@ -143,11 +160,16 @@ enum swErrorCode { SW_ERROR_SERVER_SEND_IN_MASTER, 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. */ - SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT = 9101, SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, @@ -176,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 }; @@ -185,6 +211,6 @@ class Exception { int code; const char *msg; - Exception(int code); + Exception(int code) throw(); }; -} +} // namespace swoole diff --git a/include/swoole_file.h b/include/swoole_file.h index beee86436d2..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,12 +101,20 @@ 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(); bool stat(FileStatus *_stat) const { if (::fstat(fd_, _stat) < 0) { - swSysWarn("fstat() failed"); + swoole_sys_warning("fstat() failed"); return false; } else { return true; @@ -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 6d4209fae00..94a3dbdabdd 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -10,21 +10,24 @@ | 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" -enum swHttp_version { +#include + +enum swHttpVersion { SW_HTTP_VERSION_10 = 1, SW_HTTP_VERSION_11, SW_HTTP_VERSION_2, SW_HTTP_VERSION_3, }; -enum swHttp_method { +enum swHttpMethod { SW_HTTP_DELETE = 1, SW_HTTP_GET, SW_HTTP_HEAD, @@ -59,7 +62,7 @@ enum swHttp_method { SW_HTTP_PRI, }; -enum swHttp_status_code { +enum swHttpStatusCode { SW_HTTP_CONTINUE = 100, SW_HTTP_SWITCHING_PROTOCOLS = 101, SW_HTTP_PROCESSING = 102, @@ -93,6 +96,7 @@ enum swHttp_status_code { 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 swHttp_status_code { 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,41 +146,94 @@ 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_; - swString *buffer_; + String *buffer_; public: 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); + +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(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 +} // namespace http_server } // namespace swoole - -int swHttp_get_method(const char *method_str, size_t method_len); -const char *swHttp_get_method_string(int method); -const char *swHttp_get_status_message(int code); -size_t swHttp_url_decode(char *str, size_t len); -char *swHttp_url_encode(char const *str, size_t len); - -#ifdef SW_USE_HTTP2 -ssize_t swHttpMix_get_package_length(swProtocol *protocol, swSocket *conn, const char *data, uint32_t length); -uint8_t swHttpMix_get_package_length_size(swSocket *conn); -int swHttpMix_dispatch_frame(swProtocol *protocol, swSocket *conn, const char *data, uint32_t length); -#endif diff --git a/include/swoole_http2.h b/include/swoole_http2.h index 5bc05cddba7..9f3b6e5c395 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -10,32 +10,35 @@ | 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 swHttp2_error_code { - 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, +enum swHttp2ErrorCode { + 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 swHttp2_frame_type { +enum swHttp2FrameType { SW_HTTP2_TYPE_DATA = 0, SW_HTTP2_TYPE_HEADERS = 1, SW_HTTP2_TYPE_PRIORITY = 2, @@ -48,7 +51,7 @@ enum swHttp2_frame_type { SW_HTTP2_TYPE_CONTINUATION = 9, }; -enum swHttp2_frame_flag { +enum swHttp2FrameFlag { SW_HTTP2_FLAG_NONE = 0x00, SW_HTTP2_FLAG_ACK = 0x01, SW_HTTP2_FLAG_END_STREAM = 0x01, @@ -57,7 +60,7 @@ enum swHttp2_frame_flag { SW_HTTP2_FLAG_PRIORITY = 0x20, }; -enum swHttp2_setting_id { +enum swHttp2SettingId { SW_HTTP2_SETTING_HEADER_TABLE_SIZE = 0x1, SW_HTTP2_SETTINGS_ENABLE_PUSH = 0x2, SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, @@ -66,7 +69,7 @@ enum swHttp2_setting_id { SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, }; -enum swHttp2_stream_flag { +enum swHttp2StreamFlag { SW_HTTP2_STREAM_NORMAL = 0, SW_HTTP2_STREAM_REQUEST_END = 1 << 0, SW_HTTP2_STREAM_PIPELINE_REQUEST = 1 << 1, @@ -76,6 +79,7 @@ enum swHttp2_stream_flag { #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,36 +91,31 @@ enum swHttp2_stream_flag { #define SW_HTTP2_STREAM_ID_SIZE 4 #define SW_HTTP2_SETTINGS_PARAM_SIZE 6 -#define swHttp2FrameTraceLogFlags \ - ((flags & SW_HTTP2_FLAG_ACK) ? "\nEND_ACK |" : ""), ((flags & SW_HTTP2_FLAG_END_STREAM) ? "\nEND_STREAM |" : ""), \ - ((flags & SW_HTTP2_FLAG_END_HEADERS) ? "\nEND_HEADERS |" : ""), \ - ((flags & SW_HTTP2_FLAG_PADDED) ? "\nEND_PADDED |" : ""), \ - ((flags & SW_HTTP2_FLAG_PRIORITY) ? "\nEND_PRIORITY |" : "") - -#define swHttp2FrameTraceLog(recv, str, ...) \ - swTraceLog(SW_TRACE_HTTP2, \ - "\nrecv [" \ - "\e[3" \ - "%d" \ - "m" \ - "%s" \ - "\e[0m" \ - "] frame " str "%s%s%s%s%s", \ - swoole::http2::get_type_color(type), \ - swoole::http2::get_type(type), \ - length, \ - flags, \ - stream_id, \ - ##__VA_ARGS__, \ - swHttp2FrameTraceLogFlags); +#define swoole_http2_frame_trace_log(_trace_str, ...) \ + swoole_trace_log(SW_TRACE_HTTP2, \ + 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; }; @@ -145,17 +144,45 @@ 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) { + std::string str; + if (__flags & SW_HTTP2_FLAG_ACK) { + str.append("ACK|"); + } + if (__flags & SW_HTTP2_FLAG_END_STREAM) { + str.append("END_STREAM|"); + } + if (__flags & SW_HTTP2_FLAG_END_HEADERS) { + str.append("END_HEADERS|"); + } + if (__flags & SW_HTTP2_FLAG_PADDED) { + str.append("PADDED|"); + } + if (__flags & SW_HTTP2_FLAG_PRIORITY) { + str.append("PRIORITY|"); + } + if (str.back() == '|') { + return str.substr(0, str.length() - 1); + } else { + return "none"; + } } /** 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 2099eb8e6d9..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 | +----------------------------------------------------------------------+ */ @@ -25,7 +25,7 @@ #define SW_LOG_DATE_STRLEN 128 #define SW_LOG_DEFAULT_DATE_FORMAT "%F %T" -enum swLog_level { +enum swLogLevel { SW_LOG_DEBUG = 0, SW_LOG_TRACE, SW_LOG_INFO, @@ -35,7 +35,7 @@ enum swLog_level { SW_LOG_NONE, }; -enum swLog_rotation_type { +enum swLogRotationType { SW_LOG_ROTATION_SINGLE = 0, SW_LOG_ROTATION_MONTHLY, SW_LOG_ROTATION_DAILY, @@ -87,22 +87,22 @@ class Logger { swoole::Logger *sw_logger(); #define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str()) -#define swInfo(str, ...) \ - if (SW_LOG_INFO >= sw_logger()->get_level()) { \ +#define swoole_info(str, ...) \ + 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 swNotice(str, ...) \ - if (SW_LOG_NOTICE >= sw_logger()->get_level()) { \ +#define swoole_notice(str, ...) \ + 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); \ } -#define swSysNotice(str, ...) \ +#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]", \ @@ -115,18 +115,18 @@ swoole::Logger *sw_logger(); } \ } while (0) -#define swWarn(str, ...) \ +#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); \ } \ } while (0) -#define swSysWarn(str, ...) \ +#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]", \ @@ -138,14 +138,14 @@ swoole::Logger *sw_logger(); } \ } while (0) -#define swError(str, ...) \ +#define swoole_error(str, ...) \ 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 swSysError(str, ...) \ +#define swoole_sys_error(str, ...) \ do { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ @@ -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 swFatalError(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); \ @@ -175,18 +171,18 @@ swoole::Logger *sw_logger(); } while (0) #ifdef SW_DEBUG -#define swDebug(str, ...) \ - if (SW_LOG_DEBUG >= sw_logger()->get_level()) { \ +#define swoole_debug(str, ...) \ + 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); \ } -#define swHexDump(data, length) \ +#define swoole_hex_dump(data, length) \ do { \ const char *__data = (data); \ size_t __length = (length); \ - swDebug("+----------+------------+-----------+-----------+------------+------------------+"); \ + swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ for (size_t of = 0; of < __length; of += 16) { \ char hex[16 * 3 + 1]; \ char str[16 + 1]; \ @@ -195,16 +191,16 @@ swoole::Logger *sw_logger(); hof += sprintf(hex + hof, "%02x ", (__data)[i] & 0xff); \ sof += sprintf(str + sof, "%c", isprint((int) (__data)[i]) ? (__data)[i] : '.'); \ } \ - swDebug("| %08x | %-48s| %-16s |", of, hex, str); \ + swoole_debug("| %08zx | %-48s| %-16s |", of, hex, str); \ } \ - swDebug("+----------+------------+-----------+-----------+------------+------------------+"); \ + swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ } while (0) #else -#define swDebug(str, ...) -#define swHexDump(data, length) +#define swoole_debug(str, ...) +#define swoole_hex_dump(data, length) #endif -enum swTrace_type { +enum swTraceWhat : long { /** * Server */ @@ -245,18 +241,24 @@ 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 swTraceLog(what, str, ...) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (what & SwooleG.trace_flags)) { \ +#define swoole_trace_log(what, str, ...) \ + 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); \ } #else -#define swTraceLog(what, str, ...) +#define swoole_trace_log(what, str, ...) #endif -#define swTrace(str, ...) swTraceLog(SW_TRACE_NORMAL, str, ##__VA_ARGS__) +#define swoole_trace(str, ...) swoole_trace_log(SW_TRACE_NORMAL, str, ##__VA_ARGS__) 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 7241ed75c84..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,8 +44,10 @@ 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(); static size_t sizeof_struct_slice(); static size_t sizeof_struct_impl(); }; @@ -77,6 +80,7 @@ class GlobalMemory : public MemoryPool { void free(void *ptr); void destroy(); size_t capacity(); + size_t get_memory_size(); }; } // namespace swoole 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 0a954d29cac..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 @@ -24,7 +24,14 @@ #define SW_MQTT_MAX_LENGTH_SIZE 4 #define SW_MQTT_MAX_PAYLOAD_SIZE 268435455 -enum swMqtt_opcode { +#define SW_MQTT_SETRETAIN(HDR, R) (HDR | (R)) +#define SW_MQTT_SETQOS(HDR, Q) (HDR | ((Q) << 1)) +#define SW_MQTT_SETDUP(HDR, D) (HDR | ((D) << 3)) + +namespace swoole { +namespace mqtt { + +enum Opcode { SW_MQTT_CONNECT = 0x10, SW_MQTT_CONNACK = 0x20, SW_MQTT_PUBLISH = 0x30, @@ -41,7 +48,7 @@ enum swMqtt_opcode { SW_MQTT_DISCONNECT = 0xE0, }; -struct swMqtt_packet { +struct Packet { uint8_t type : 4; uint8_t dup : 1; uint8_t qos : 2; @@ -50,9 +57,7 @@ struct swMqtt_packet { char protocol_name[8]; }; -#define SETRETAIN(HDR, R) (HDR | (R)) -#define SETQOS(HDR, Q) (HDR | ((Q) << 1)) -#define SETDUP(HDR, D) (HDR | ((D) << 3)) - -ssize_t swMqtt_get_package_length(swProtocol *protocol, swSocket *conn, const char *data, uint32_t size); -void swMqtt_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 65b4d130f6e..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,14 +26,15 @@ #include "swoole_pipe.h" #include "swoole_channel.h" #include "swoole_msg_queue.h" +#include "swoole_message_bus.h" -enum swWorker_status { +enum swWorkerStatus { SW_WORKER_BUSY = 1, SW_WORKER_IDLE = 2, SW_WORKER_EXIT = 3, }; -enum swIPC_type { +enum swIPCMode { SW_IPC_NONE = 0, SW_IPC_UNIXSOCK = 1, SW_IPC_MSGQUEUE = 2, @@ -42,6 +43,21 @@ enum swIPC_type { namespace swoole { +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; +}; + class ExitStatus { private: pid_t pid_; @@ -87,27 +103,30 @@ struct ProcessPool; struct Worker; struct WorkerGlobal { - bool run_always; bool shutdown; + bool running; uint32_t max_request; - String **output_buffer; + /** + * 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; }; 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; @@ -118,27 +137,18 @@ 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; - long dispatch_count; - long request_count; + sw_atomic_long_t dispatch_count; + sw_atomic_long_t request_count; + sw_atomic_long_t response_count; size_t coroutine_num; - /** - * worker id - */ - uint32_t id; - Mutex *lock; - UnixSocket *pipe_object; network::Socket *pipe_master; @@ -149,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 swWorker_status _status) { + 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; } @@ -180,11 +213,16 @@ struct ProcessPool { bool reload_init; bool read_message; bool started; - uint8_t dispatch_mode; + 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; + uint64_t reload_count; + time_t reload_last_time; Worker *reload_workers; /** @@ -225,10 +263,15 @@ 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); @@ -237,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++) { @@ -252,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++) { @@ -260,7 +303,7 @@ struct ProcessPool { } } - inline Worker *get_worker(int worker_id) { + Worker *get_worker(int worker_id) { return &(workers[worker_id - start_id]); } @@ -272,26 +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, swIPC_type ipc_mode = SW_IPC_NONE); + 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 @@ -307,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 a94279762a0..987624228a7 100644 --- a/include/swoole_proxy.h +++ b/include/swoole_proxy.h @@ -10,23 +10,46 @@ | 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" -enum swHttp_proxy_state { +#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, SW_HTTP_PROXY_STATE_HANDSHAKE, SW_HTTP_PROXY_STATE_READY, }; +enum swSocks5State { + SW_SOCKS5_STATE_WAIT = 0, + SW_SOCKS5_STATE_HANDSHAKE, + SW_SOCKS5_STATE_AUTH, + SW_SOCKS5_STATE_CONNECT, + SW_SOCKS5_STATE_READY, +}; + +enum swSocks5Method { + SW_SOCKS5_METHOD_AUTH = 0x02, +}; + namespace swoole { +class String; + struct HttpProxy { uint8_t state; uint8_t dont_handshake; @@ -36,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 { @@ -63,15 +87,3 @@ struct Socks5Proxy { } }; } // namespace swoole - -enum swSocks5_state { - SW_SOCKS5_STATE_WAIT = 0, - SW_SOCKS5_STATE_HANDSHAKE, - SW_SOCKS5_STATE_AUTH, - SW_SOCKS5_STATE_CONNECT, - SW_SOCKS5_STATE_READY, -}; - -enum swSocks5_method { - SW_SOCKS5_METHOD_AUTH = 0x02, -}; diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index 8fda4ccb935..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 | +----------------------------------------------------------------------+ */ @@ -22,6 +22,7 @@ #include #include +#include namespace swoole { @@ -51,13 +52,18 @@ class ReactorImpl { class CallbackManager { public: - inline void append(Callback fn, void *private_data) { + typedef std::list> TaskList; + void append(Callback fn, void *private_data) { list_.emplace_back(fn, private_data); } - inline void prepend(Callback fn, void *private_data) { + void prepend(Callback fn, void *private_data) { list_.emplace_front(fn, private_data); + auto t = list_.back(); } - inline void execute() { + void remove(TaskList::iterator iter) { + list_.erase(iter); + } + void execute() { while (!list_.empty()) { std::pair task = list_.front(); list_.pop_front(); @@ -66,7 +72,7 @@ class CallbackManager { } protected: - std::list> list_; + TaskList list_; }; class Reactor { @@ -98,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, }; @@ -111,7 +119,6 @@ class Reactor { */ int singal_no = 0; - uint32_t event_num = 0; uint32_t max_event_num = 0; bool running = false; @@ -120,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 */ @@ -168,14 +172,15 @@ class Reactor { std::function onBegin; - int (*write)(Reactor *reactor, network::Socket *socket, const void *buf, size_t n) = nullptr; - int (*writev)(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt) = nullptr; + ssize_t (*write)(Reactor *reactor, network::Socket *socket, const void *buf, size_t n) = nullptr; + ssize_t (*writev)(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt) = nullptr; int (*close)(Reactor *reactor, network::Socket *socket) = nullptr; private: ReactorImpl *impl; std::map> end_callbacks; - std::map> exit_conditions; + std::map> exit_conditions; + std::unordered_map sockets_; public: Reactor(int max_event = SW_REACTOR_MAXEVENTS, Type _type = TYPE_AUTO); @@ -183,7 +188,7 @@ class Reactor { bool if_exit(); void defer(Callback cb, void *data = nullptr); void set_end_callback(enum EndCallback id, const std::function &fn); - void set_exit_condition(enum ExitCondition id, const std::function &fn); + void set_exit_condition(enum ExitCondition id, const std::function &fn); bool set_handler(int _fdtype, ReactorHandler handler); void add_destroy_callback(Callback cb, void *data = nullptr); void execute_end_callbacks(bool timedout = false); @@ -193,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, enum swEvent_type 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, enum swEvent_type 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); @@ -228,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); @@ -237,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); @@ -246,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); @@ -255,15 +260,33 @@ 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; } - inline ReactorHandler get_handler(enum swEvent_type event_type, enum swFd_type fd_type) { + size_t get_event_num() { + return sockets_.size(); + } + + const std::unordered_map &get_sockets() { + return sockets_; + } + + network::Socket *get_socket(int fd) { + return sockets_[fd]; + } + + void foreach_socket(const std::function &callback) { + for (auto &kv : sockets_) { + callback(kv.first, kv.second); + } + } + + ReactorHandler get_handler(EventType event_type, FdType fd_type) { switch (event_type) { case SW_EVENT_READ: return read_handler[fd_type]; @@ -278,7 +301,7 @@ class Reactor { return nullptr; } - inline ReactorHandler get_error_handler(enum swFd_type 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) { @@ -290,32 +313,32 @@ 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; - event_num++; + 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; - event_num--; + sockets_.erase(_socket->fd); } bool catch_error() { @@ -326,15 +349,15 @@ class Reactor { return false; } - static int _write(Reactor *reactor, network::Socket *socket, const void *buf, size_t n); - static int _writev(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt); + static ssize_t _write(Reactor *reactor, network::Socket *socket, const void *buf, size_t n); + static ssize_t _writev(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt); static int _close(Reactor *reactor, network::Socket *socket); static int _writable_callback(Reactor *reactor, Event *ev); void activate_future_task(); - static enum swFd_type get_fd_type(int flags) { - return (enum swFd_type)(flags & (~SW_EVENT_READ) & (~SW_EVENT_WRITE) & (~SW_EVENT_ERROR) & (~SW_EVENT_ONCE)); + static FdType get_fd_type(int flags) { + return (FdType)(flags & (~SW_EVENT_READ) & (~SW_EVENT_WRITE) & (~SW_EVENT_ERROR) & (~SW_EVENT_ONCE)); } static bool isset_read_event(int events) { diff --git a/include/swoole_redis.h b/include/swoole_redis.h index f12dfc38be9..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 | +----------------------------------------------------------------------+ */ @@ -21,29 +21,32 @@ #include #include -enum swRedis_receive_state { - SW_REDIS_RECEIVE_TOTAL_LINE, - SW_REDIS_RECEIVE_LENGTH, - SW_REDIS_RECEIVE_STRING, -}; - -enum swRedis_reply_type { - SW_REDIS_REPLY_ERROR, - SW_REDIS_REPLY_NIL, - SW_REDIS_REPLY_STATUS, - SW_REDIS_REPLY_INT, - SW_REDIS_REPLY_STRING, - SW_REDIS_REPLY_SET, - SW_REDIS_REPLY_MAP, -}; - #define SW_REDIS_RETURN_NIL "$-1\r\n" #define SW_REDIS_MAX_COMMAND_SIZE 64 #define SW_REDIS_MAX_LINES 128 #define SW_REDIS_MAX_STRING_SIZE 536870912 // 512M -static sw_inline const char *swRedis_get_number(const char *p, int *_ret) { +namespace swoole { +namespace redis { + +enum State { + STATE_RECEIVE_TOTAL_LINE, + STATE_RECEIVE_LENGTH, + STATE_RECEIVE_STRING, +}; + +enum ReplyType { + REPLY_ERROR, + REPLY_NIL, + REPLY_STATUS, + REPLY_INT, + REPLY_STRING, + REPLY_SET, + REPLY_MAP, +}; + +static sw_inline const char *get_number(const char *p, int *_ret) { char *endptr; p++; int ret = strtol(p, &endptr, 10); @@ -56,8 +59,11 @@ static sw_inline const char *swRedis_get_number(const char *p, int *_ret) { } } -int swRedis_recv_packet(swProtocol *protocol, swConnection *conn, swString *buffer); -std::vector swRedis_parse(const char *data, size_t len); -bool swRedis_format(swString *buf); -bool swRedis_format(swString *buf, enum swRedis_reply_type type, const std::string &value); -bool swRedis_format(swString *buf, enum swRedis_reply_type type, long value); +int recv_packet(Protocol *protocol, Connection *conn, String *buffer); +std::vector parse(const char *data, size_t len); +bool format(String *buf); +bool format(String *buf, enum ReplyType type, const std::string &value); +bool format(String *buf, enum ReplyType type, long value); + +} // namespace redis +} // namespace swoole diff --git a/include/swoole_server.h b/include/swoole_server.h index 7ee38be73e4..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,40 +46,7 @@ #include #include #include - -#define SW_REACTOR_NUM SW_CPU_NUM -#define SW_WORKER_NUM (SW_CPU_NUM * 2) - -enum swTask_ipc_mode { - SW_TASK_IPC_UNIXSOCK = 1, - SW_TASK_IPC_MSGQUEUE = 2, - SW_TASK_IPC_PREEMPTIVE = 3, - SW_TASK_IPC_STREAM = 4, -}; - -enum swFactory_dispatch_mode { - SW_DISPATCH_ROUND = 1, - SW_DISPATCH_FDMOD = 2, - SW_DISPATCH_QUEUE = 3, - SW_DISPATCH_IPMOD = 4, - SW_DISPATCH_UIDMOD = 5, - SW_DISPATCH_USERFUNC = 6, - SW_DISPATCH_STREAM = 7, - SW_DISPATCH_CO_CONN_LB, - SW_DISPATCH_CO_REQ_LB, -}; - -enum swFactory_dispatch_result { - SW_DISPATCH_RESULT_DISCARD_PACKET = -1, - SW_DISPATCH_RESULT_CLOSE_CONNECTION = -2, - SW_DISPATCH_RESULT_USERFUNC_FALLBACK = -3, -}; - -enum swThread_type { - SW_THREAD_MASTER = 1, - SW_THREAD_REACTOR = 2, - SW_THREAD_HEARTBEAT = 3, -}; +#include //------------------------------------Server------------------------------------------- namespace swoole { @@ -84,6 +55,11 @@ namespace http_server { struct Request; } +class Server; +struct Manager; + +typedef std::function WorkerFn; + struct Session { SessionId id; int fd; @@ -98,7 +74,7 @@ struct Connection { * system fd must be 0. en: signalfd, listen socket */ uint8_t active; - enum swSocket_type socket_type; + SocketType socket_type; int fd; int worker_id; SessionId session_id; @@ -110,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 @@ -168,58 +142,37 @@ 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; }; +//------------------------------------ReactorThread------------------------------------------- struct ReactorThread { + int id; std::thread thread; network::Socket *notify_pipe = nullptr; - uint32_t pipe_num = 0; - network::Socket *pipe_sockets = nullptr; - std::unordered_map send_buffers; -}; - -struct WorkerStopMessage { - pid_t pid; - uint16_t worker_id; + uint64_t dispatch_count = 0; + 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 SendData { - DataHead info; - const char *data; -}; - -struct RecvData { - DataHead info; - const char *data; -}; - -struct PipeBuffer { - DataHead info; - char data[0]; -}; - -struct DgramPacket { - enum swSocket_type socket_type; - network::Address socket_addr; - uint32_t length; - char data[0]; -}; - -//------------------------------------Packet------------------------------------------- -struct PacketTask { - size_t length; - char tmpfile[SW_TASK_TMP_PATH_SIZE]; -}; - -struct PacketPtr { - DataHead info; - struct { - uint32_t length; - char *str; - } data; +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; + sw_atomic_long_t dispatch_count; + sw_atomic_long_t request_count; + sw_atomic_long_t response_count; + sw_atomic_long_t total_recv_bytes; + sw_atomic_long_t total_send_bytes; }; struct ListenPort { @@ -245,13 +198,13 @@ 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; uint32_t buffer_low_watermark = 0; - enum swSocket_type type = SW_SOCK_TCP; + SocketType type = SW_SOCK_TCP; uint8_t ssl = 0; std::string host; int port = 0; @@ -333,41 +286,49 @@ struct ListenPort { #endif #endif - sw_atomic_t *connection_num = nullptr; + ServerPortGS *gs = nullptr; Protocol protocol = {}; void *ptr = nullptr; - int (*onRead)(Reactor *reactor, ListenPort *port, swEvent *event) = nullptr; + 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); bool ssl_create(Connection *conn, network::Socket *sock); @@ -377,27 +338,28 @@ struct ListenPort { void ssl_set_key_file(const std::string &file) { ssl_context->key_file = file; } - void ssl_set_cert_file(const std::string &file) { ssl_context->cert_file = file; } #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(); } - enum swSocket_type 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 { @@ -411,23 +373,32 @@ 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; - sw_atomic_long_t request_count; sw_atomic_long_t dispatch_count; + sw_atomic_long_t request_count; + sw_atomic_long_t response_count; + 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; + Barrier manager_barrier; + ProcessPool task_workers; ProcessPool event_workers; }; -class Server; -struct Manager; - class Factory { protected: Server *server_; @@ -436,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; @@ -447,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; @@ -455,13 +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; - PipeBuffer *send_buffer; - public: ProcessFactory(Server *server); ~ProcessFactory(); @@ -473,11 +448,42 @@ 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, SW_SERVER_EVENT_RECV_DGRAM, // send data + SW_SERVER_EVENT_SEND_DATA, SW_SERVER_EVENT_SEND_FILE, // connection event SW_SERVER_EVENT_CLOSE, @@ -488,9 +494,6 @@ enum ServerEventType { SW_SERVER_EVENT_FINISH, // pipe SW_SERVER_EVENT_PIPE_MESSAGE, - // proxy - SW_SERVER_EVENT_PROXY_START, - SW_SERVER_EVENT_PROXY_END, // event operate SW_SERVER_EVENT_PAUSE_RECV, SW_SERVER_EVENT_RESUME_RECV, @@ -500,15 +503,67 @@ enum ServerEventType { // process message SW_SERVER_EVENT_INCOMING, SW_SERVER_EVENT_SHUTDOWN, + SW_SERVER_EVENT_COMMAND_REQUEST, + SW_SERVER_EVENT_COMMAND_RESPONSE, }; class Server { public: typedef int (*DispatchFunction)(Server *, Connection *, SendData *); + struct Command { + typedef std::function Callback; + typedef std::function Handler; + enum ProcessType { + MASTER = 1u << 1, + REACTOR_THREAD = 1u << 2, + EVENT_WORKER = 1u << 3, + TASK_WORKER = 1u << 4, + MANAGER = 1u << 5, + ALL_PROCESS = MASTER | REACTOR_THREAD | EVENT_WORKER | TASK_WORKER | MANAGER, + }; + int id; + int accepted_process_types; + std::string name; + }; + enum Mode { MODE_BASE = 1, MODE_PROCESS = 2, + MODE_THREAD = 3, + }; + + enum TaskIpcMode { + TASK_IPC_UNIXSOCK = 1, + TASK_IPC_MSGQUEUE = 2, + TASK_IPC_PREEMPTIVE = 3, + TASK_IPC_STREAM = 4, + }; + + enum ThreadType { + THREAD_NORMAL = 0, + THREAD_MASTER = 1, + THREAD_REACTOR = 2, + THREAD_HEARTBEAT = 3, + THREAD_WORKER = 4, + }; + + enum DispatchMode { + DISPATCH_ROUND = 1, + DISPATCH_FDMOD = 2, + DISPATCH_IDLE_WORKER = 3, + DISPATCH_IPMOD = 4, + DISPATCH_UIDMOD = 5, + DISPATCH_USERFUNC = 6, + DISPATCH_CO_CONN_LB = 8, + DISPATCH_CO_REQ_LB = 9, + DISPATCH_CONCURRENT_LB = 10, + }; + + enum FactoryDispatchResult { + DISPATCH_RESULT_DISCARD_PACKET = -1, + DISPATCH_RESULT_CLOSE_CONNECTION = -2, + DISPATCH_RESULT_USERFUNC_FALLBACK = -3, }; enum HookType { @@ -527,6 +582,7 @@ class Server { HOOK_MANAGER_START, HOOK_MANAGER_TIMER, HOOK_PROCESS_TIMER, + HOOK_END = SW_MAX_HOOK_TYPE - 1, }; enum CloseFlag { @@ -548,7 +604,7 @@ class Server { /** * package dispatch mode */ - uint8_t dispatch_mode = SW_DISPATCH_FDMOD; + uint8_t dispatch_mode = DISPATCH_FDMOD; /** * No idle work process is available. @@ -570,11 +626,9 @@ 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--------------------------------*/ - const Allocator *worker_buffer_allocator; - std::unordered_map> worker_buffers; - std::atomic worker_msg_id; sw_atomic_t worker_round_id = 0; /** @@ -684,7 +738,9 @@ class Server { int *cpu_affinity_available = 0; int cpu_affinity_available_num = 0; - PipeBuffer **pipe_buffers = nullptr; + UnixSocket *pipe_command = nullptr; + MessageBus message_bus; + double send_timeout = 0; uint16_t heartbeat_check_interval = 0; @@ -710,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; @@ -729,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; @@ -745,66 +819,91 @@ 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; } + 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] + */ + 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. + */ + int pipe_worker_id = reactor_id + (pipe_index * reactor_num); + Worker *worker = get_worker(pipe_worker_id); + return worker->pipe_worker; + } + /** * task process */ uint32_t task_worker_num = 0; - uint8_t task_ipc_mode = SW_TASK_IPC_UNIXSOCK; + uint8_t task_ipc_mode = TASK_IPC_UNIXSOCK; 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::unordered_map user_worker_map; + /** + * Shared memory, sharing state between processes */ - uint32_t user_worker_num = 0; - std::vector *user_worker_list = nullptr; - std::unordered_map *user_worker_map = nullptr; Worker *user_workers = nullptr; + std::unordered_map commands; + std::unordered_map command_handlers; + std::unordered_map command_callbacks; + int command_current_id = 1; + int64_t command_current_request_id = 1; + Worker *workers = nullptr; - Channel *message_box = 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 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; - const Allocator *buffer_allocator = &SwooleG.std_allocator; + const Allocator *recv_buffer_allocator = &SwooleG.std_allocator; size_t recv_buffer_size = SW_BUFFER_SIZE_BIG; int manager_alarm = 0; @@ -816,26 +915,28 @@ class Server { void *hooks[SW_MAX_HOOK_TYPE] = {}; + /*----------------------------Event Callback--------------------------------*/ /** * Master Process */ std::function onStart; + std::function onBeforeShutdown; std::function onShutdown; /** * Manager Process */ 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 @@ -852,31 +953,9 @@ class Server { std::function onTask; std::function onFinish; /** - * Chunk control + * for MessageBus */ - size_t get_packet(EventData *req, char **data_ptr); - - String *get_worker_buffer(DataHead *info) { - auto iter = worker_buffers.find(info->msg_id); - if (iter == worker_buffers.end()) { - if (info->flags & SW_EVENT_DATA_BEGIN) { - auto buffer = make_string(info->len, worker_buffer_allocator); - worker_buffers.emplace(info->msg_id, std::shared_ptr(buffer)); - return buffer; - } - return nullptr; - } - return iter->second.get(); - } - - void pop_worker_buffer(DataHead *info) { - uint64_t msg_id = info->msg_id; - auto iter = worker_buffers.find(msg_id); - if (iter != worker_buffers.end()) { - iter->second.get()->str = nullptr; - } - } - + std::function msg_id_generator; /** * Hook */ @@ -888,13 +967,13 @@ class Server { bool set_document_root(const std::string &path) { if (path.length() > PATH_MAX) { - swWarn("The length of document_root must be less than %d", PATH_MAX); + swoole_warning("The length of document_root must be less than %d", PATH_MAX); return false; } char _realpath[PATH_MAX]; if (!realpath(path.c_str(), _realpath)) { - swWarn("document_root[%s] does not exist", path.c_str()); + swoole_warning("document_root[%s] does not exist", path.c_str()); return false; } @@ -905,48 +984,87 @@ 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(enum swSocket_type type, const char *host, int port); + ListenPort *add_port(SocketType type, const char *host, int port); int add_systemd_socket(); int add_hook(enum HookType type, const Callback &func, int push_back); + 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_tasking_num(); + + 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()]); + } - inline int get_minfd() { + 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, buffer_allocator); + buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, recv_buffer_allocator); if (!buffer) { return nullptr; } @@ -956,41 +1074,39 @@ class Server { return buffer; } - inline uint32_t get_worker_buffer_num() { - return is_base_mode() ? 1 : reactor_num + dgram_port_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 } - /** - * reactor_id: The fd in which the reactor. - */ - inline swSocket *get_reactor_thread_pipe(SessionId session_id, int reactor_id) { - int pipe_index = session_id % reactor_pipe_num; - /** - * pipe_worker_id: The pipe in which worker. - */ - int pipe_worker_id = reactor_id + (pipe_index * reactor_num); - Worker *worker = get_worker(pipe_worker_id); - return worker->pipe_worker; + 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 != SW_DISPATCH_ROUND && dispatch_mode != SW_DISPATCH_QUEUE && - dispatch_mode != SW_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()) { @@ -1000,16 +1116,20 @@ class Server { } } - inline bool is_hash_dispatch_mode() { - return dispatch_mode == SW_DISPATCH_FDMOD || dispatch_mode == SW_DISPATCH_IPMOD || - dispatch_mode == SW_DISPATCH_CO_CONN_LB; + bool is_master_thread() { + return swoole_get_thread_type() == Server::THREAD_MASTER; } - inline bool is_support_send_yield() { + bool is_hash_dispatch_mode() { + return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || + dispatch_mode == DISPATCH_CO_CONN_LB; + } + + 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 @@ -1017,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 @@ -1025,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]); @@ -1038,7 +1162,7 @@ class Server { } // User Worker - uint32_t user_worker_max = task_worker_max + user_worker_num; + uint32_t user_worker_max = task_worker_max + user_worker_list.size(); if (worker_id < user_worker_max) { return &(user_workers[worker_id - task_worker_max]); } @@ -1046,55 +1170,86 @@ 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 + user_worker_num; + return get_core_worker_num() + get_user_worker_num(); + } + + size_t get_user_worker_num() { + return user_worker_list.size(); + } + + size_t get_core_worker_num() { + return worker_num + task_worker_num; } - inline ReactorThread *get_thread(int reactor_id) { + ReactorThread *get_thread(int reactor_id) { return &reactor_threads[reactor_id]; } - inline bool is_started() { + bool is_started() { return gs->start; } + bool is_created() { + 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 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() { @@ -1106,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); } @@ -1129,6 +1284,8 @@ class Server { static int is_stream_event(uint8_t type) { switch (type) { case SW_SERVER_EVENT_RECV_DATA: + case SW_SERVER_EVENT_SEND_DATA: + case SW_SERVER_EVENT_SEND_FILE: case SW_SERVER_EVENT_CONNECT: case SW_SERVER_EVENT_CLOSE: case SW_SERVER_EVENT_PAUSE_RECV: @@ -1141,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); @@ -1158,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) { @@ -1168,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 @@ -1188,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); @@ -1214,23 +1362,28 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); - + 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); - - int accept_task(EventData *task); static int accept_connection(Reactor *reactor, Event *event); #ifdef SW_SUPPORT_DTLS dtls::Session *accept_dtls_connection(ListenPort *ls, network::Address *sa); #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_master(Worker *worker, const iovec *iov, size_t iovcnt); 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, data->size(), flags); + } + ssize_t send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id); - ssize_t send_to_reactor_thread(const DataHead *head, const iovec *iov, size_t iovcnt, 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); @@ -1239,20 +1392,61 @@ class Server { bool notify(Connection *conn, enum ServerEventType event); bool feedback(Connection *conn, enum ServerEventType event); + bool command(WorkerId process_id, + Command::ProcessType process_type, + const std::string &name, + 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_ipc_max_size(); 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; @@ -1261,6 +1455,7 @@ class Server { } int create_pipe_buffers(); + void release_pipe_buffers(); void create_worker(Worker *worker); void destroy_worker(Worker *worker); void disable_accept(); @@ -1268,36 +1463,62 @@ 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_; Connection *connection_list = nullptr; Session *session_list = nullptr; - uint32_t *port_connnection_num_list = nullptr; + ServerPortGS *port_gs_list = nullptr; /** * http static file directory */ @@ -1311,27 +1532,78 @@ 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_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 typedef swoole::Server swServer; typedef swoole::ListenPort swListenPort; -typedef swoole::Connection swConnection; typedef swoole::RecvData swRecvData; extern swoole::Server *g_server_instance; diff --git a/include/swoole_signal.h b/include/swoole_signal.h index ad3d8af7d0e..5416dedce5d 100644 --- a/include/swoole_signal.h +++ b/include/swoole_signal.h @@ -10,32 +10,38 @@ | 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.h" + #include -typedef void (*swSignalHandler)(int); +namespace swoole { +typedef void (*SignalHandler)(int); -struct swSignal { - swSignalHandler handler; +struct Signal { + SignalHandler handler; uint16_t signo; bool activated; }; +} // namespace swoole + +typedef swoole::SignalHandler swSignalHandler; #ifdef HAVE_SIGNALFD -void swSignalfd_init(); -bool swSignalfd_setup(swoole::Reactor *reactor); +void swoole_signalfd_init(); #endif -swSignalHandler swSignal_set(int signo, swSignalHandler func); -swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int mask); -swSignalHandler swSignal_get_handler(int signo); -void swSignal_clear(void); -void swSignal_none(void); -char *swSignal_str(int sig); -void swSignal_callback(int signo); +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); + +SW_API void swoole_signal_clear(void); +SW_API void swoole_signal_block_all(void); +SW_API char *swoole_signal_to_str(int sig); +SW_API void swoole_signal_callback(int signo); diff --git a/include/swoole_socket.h b/include/swoole_socket.h index c50c7187ef1..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; @@ -94,15 +94,27 @@ struct Address { struct sockaddr_un un; } addr; socklen_t len; - enum swSocket_type type; + SocketType type; + + bool assign(SocketType _type, const std::string &_host, int _port); + bool assign(const std::string &url); - bool assign(enum swSocket_type _type, const std::string &_host, int _port); 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; } }; @@ -156,8 +168,8 @@ struct Socket { static uint32_t default_buffer_size; int fd; - enum swFd_type fd_type; - enum swSocket_type socket_type; + FdType fd_type; + SocketType socket_type; int events; bool enable_tcp_nodelay; @@ -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); @@ -360,13 +380,13 @@ struct Socket { } int ssl_create(SSLContext *_ssl_context, int _flags); int ssl_connect(); - enum swReturn_code ssl_accept(); + ReturnCode ssl_accept(); ssize_t ssl_recv(void *__buf, size_t __n); 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,41 +399,41 @@ 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; } #ifdef TCP_CORK if (set_tcp_nopush(1) < 0) { - swSysWarn("set_tcp_nopush(fd=%d, ON) failed", fd); + swoole_sys_warning("set_tcp_nopush(fd=%d, ON) failed", fd); return false; } #endif // Need to turn off tcp nodelay when using nopush if (tcp_nodelay && set_tcp_nodelay(0) != 0) { - swSysWarn("set_tcp_nodelay(fd=%d, OFF) failed", fd); + swoole_sys_warning("set_tcp_nodelay(fd=%d, OFF) failed", fd); } return true; } - inline bool uncork() { + bool uncork() { if (!tcp_nopush) { return false; } #ifdef TCP_CORK if (set_tcp_nopush(0) < 0) { - swSysWarn("set_tcp_nopush(fd=%d, OFF) failed", fd); + swoole_sys_warning("set_tcp_nopush(fd=%d, OFF) failed", fd); return false; } #endif // Restore tcp_nodelay setting if (enable_tcp_nodelay && tcp_nodelay == 0 && set_tcp_nodelay(1) != 0) { - swSysWarn("set_tcp_nodelay(fd=%d, ON) failed", fd); + swoole_sys_warning("set_tcp_nodelay(fd=%d, ON) failed", fd); return false; } return true; @@ -430,11 +450,11 @@ struct Socket { int wait_event(int timeout_ms, int events); void free(); - static inline int is_dgram(swSocket_type type) { + static inline int is_dgram(SocketType type) { return (type == SW_SOCK_UDP || type == SW_SOCK_UDP6 || type == SW_SOCK_UNIX_DGRAM); } - static inline int is_stream(swSocket_type type) { + static inline int is_stream(SocketType type) { return (type == SW_SOCK_TCP || type == SW_SOCK_TCP6 || type == SW_SOCK_UNIX_STREAM); } @@ -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,20 +556,48 @@ struct Socket { } } - static inline enum swSocket_type 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; } } - static inline enum swSocket_type convert_to_type(std::string &host) { + static inline SocketType convert_to_type(std::string &host) { if (host.compare(0, 6, "unix:/", 0, 6) == 0) { host = host.substr(sizeof("unix:") - 1); host.erase(0, host.find_first_not_of('/') - 1); @@ -549,7 +609,7 @@ struct Socket { } } - static inline int get_domain_and_type(enum swSocket_type type, int *sock_domain, int *sock_type) { + static inline int get_domain_and_type(SocketType type, int *sock_domain, int *sock_type) { switch (type) { case SW_SOCK_TCP6: *sock_domain = AF_INET6; @@ -587,11 +647,31 @@ int gethostbyname(int type, const char *name, char *addr); int getaddrinfo(GetaddrinfoRequest *req); } // namespace network -network::Socket *make_socket(int fd, enum swFd_type fd_type); -network::Socket *make_socket(enum swSocket_type socket_type, enum swFd_type fd_type, int flags); -network::Socket *make_server_socket(enum swSocket_type socket_type, + +/** + * 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 d212a0fa461..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 | +----------------------------------------------------------------------+ */ @@ -33,6 +33,7 @@ #include #include #include +#include #if OPENSSL_VERSION_NUMBER >= 0x10100000L #define SW_SUPPORT_DTLS @@ -42,18 +43,27 @@ #undef SW_SUPPORT_DTLS #endif -enum swSSL_create_flag { +#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, }; -enum swSSL_state { +enum swSSLState { SW_SSL_STATE_HANDSHAKE = 0, SW_SSL_STATE_READY = 1, SW_SSL_STATE_WAIT_STREAM = 2, }; -enum swSSL_version { +enum swSSLVersion { SW_SSL_SSLv2 = 1u << 1, SW_SSL_SSLv3 = 1u << 2, SW_SSL_TLSv1 = 1u << 3, @@ -65,7 +75,7 @@ enum swSSL_version { #define SW_SSL_ALL (SW_SSL_SSLv2 | SW_SSL_SSLv3 | SW_SSL_TLSv1 | SW_SSL_TLSv1_1 | SW_SSL_TLSv1_2 | SW_SSL_TLSv1_3) -enum swSSL_method { +enum swSSLMethod { SW_SSLv23_METHOD = 0, SW_SSLv3_METHOD, SW_SSLv3_SERVER_METHOD, @@ -112,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; @@ -136,7 +151,7 @@ struct SSLContext { bool set_cert_file(const std::string &_cert_file) { if (access(_cert_file.c_str(), R_OK) < 0) { - swWarn("ssl cert file[%s] not found", _cert_file.c_str()); + swoole_warning("ssl cert file[%s] not found", _cert_file.c_str()); return false; } cert_file = _cert_file; @@ -145,7 +160,7 @@ struct SSLContext { bool set_key_file(const std::string &_key_file) { if (access(_key_file.c_str(), R_OK) < 0) { - swWarn("ssl key file[%s] not found", _key_file.c_str()); + swoole_warning("ssl key file[%s] not found", _key_file.c_str()); return false; } key_file = _key_file; @@ -162,11 +177,13 @@ struct SSLContext { }; } -void swSSL_init(void); -void swSSL_init_thread_safety(); -bool swSSL_is_thread_safety(); -void swSSL_server_http_advise(swoole::SSLContext &); -const char *swSSL_get_error(); -int swSSL_get_ex_connection_index(); -int swSSL_get_ex_port_index(); +void swoole_ssl_init(void); +void swoole_ssl_init_thread_safety(); +bool swoole_ssl_is_thread_safety(); +void swoole_ssl_server_http_advise(swoole::SSLContext &); +const char *swoole_ssl_get_error(); +int swoole_ssl_get_ex_connection_index(); +int swoole_ssl_get_ex_port_index(); +std::string swoole_ssl_get_version_message(); + #endif 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 7a104aae8e1..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,37 +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(const String &append_str) { + int append(char c) { + return append(&c, sizeof(c)); + } + + int append(const String &append_str) { size_t new_size = length + append_str.length; if (new_size > size) { if (!reserve(new_size)) { @@ -215,10 +236,10 @@ class String { return SW_OK; } - inline void write(off_t _offset, swString *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); @@ -227,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); @@ -239,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 19503f8fc95..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; @@ -178,29 +175,33 @@ class Table { void *memory; -#ifdef SW_TABLE_DEBUG - int conflict_count; - int insert_count; - int conflict_max_level; -#endif - public: std::vector *column_list; + size_t conflict_count; + sw_atomic_long_t insert_count; + sw_atomic_long_t delete_count; + 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(); bool add_column(const std::string &name, enum TableColumn::Type type, size_t size); TableRow *set(const char *key, uint16_t keylen, TableRow **rowlock, int *out_flags); 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; } @@ -212,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; @@ -238,7 +231,7 @@ class Table { } } - size_t count() { + size_t count() const { return row_num; } @@ -263,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; @@ -270,28 +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); -#ifdef SW_TABLE_DEBUG - insert_count++; -#endif + } + + 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 88b8c4832cb..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 | +----------------------------------------------------------------------+ */ @@ -41,6 +41,7 @@ struct TimerNode { enum Type type; int64_t exec_msec; int64_t interval; + uint64_t exec_count; uint64_t round; bool removed; HeapNode *heap_node; @@ -61,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_; @@ -74,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; @@ -94,7 +96,7 @@ class Timer { return msec1 + msec2; } - inline Reactor *get_reactor() { + Reactor *get_reactor() { return reactor_; } @@ -112,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; @@ -121,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 7e57533779c..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 7 +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "4.7.0" -#define SWOOLE_VERSION_ID 40700 -#define SWOOLE_API_VERSION_ID 0x202107a +#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 54e64931cf6..00000000000 --- a/make.sh +++ /dev/null @@ -1,83 +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 library [dev]" - echo "./make.sh" - exit 0 -fi - -phpize -if [ "$1" = "debug" ] ;then - ./configure ${COMPILE_PARAMS} --enable-debug-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 1268fae4839..c1cda6df0be 100644 --- a/package.xml +++ b/package.xml @@ -1,12 +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 @@ -17,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 @@ -27,20 +22,20 @@ Tianfeng Han tianfenghan - mikan.tenny@gmail.com + rango@swoole.com yes Twosee twosee - twose@qq.com + twosee@php.net yes Shen Zhe shenzhe shenzhe163@gmail.com - yes + no Lu Fei @@ -48,11 +43,17 @@ lufei@php.net yes - 2021-07-16 - + + Bruce Dou + doubaokun + doubaokun@php.net + yes + + 2024-12-16 + - 4.7.0 - 4.0 + 6.0.0 + 6.0 stable @@ -60,64 +61,128 @@ Apache2.0 - New APIs - --- - * Added Process\Pool::detach() (#4221) (@matyhtf) - * Added onDisconnect callback for Swoole\Server (#4230) (@matyhtf) - * Added Coroutine::cancel() (#4247) (#4249) (@matyhtf) - * Added http_compression/body_decompression options for Http Client (#4299) (@matyhtf) - - Enhancement - --- - * Supported mysql client prepare field type identification (#4238) (@Yurunsoft) - * Supported c-ares, Refactor DNS (#4275) (@matyhtf) - * Supported setting different idle time for each port (#4290) (@matyhtf) - * Supported SW_DISPATCH_CO_CONN_LB and SW_DISPATCH_CO_REQ_LB for Swoole\Server dispatch_mode (#4318) (@matyhtf) - * Supported timeout for Swoole\ConnectionPool::get (swoole/library#108) (@leocavalcante) - * Supported CURLOPT_PRIVATE for Hook Curl (swoole/library#112) (@sy-records) - * Optimized PDOStatementProxy::setFetchMode function prototype (swoole/library#109) (@yespire) - - Fixed - --- - * Fixed uncaught thread creation exception when creating a large number of coroutines (swoole/swoole-src@8ce5041) (@matyhtf) - * Fixed the "make install" missing php_swoole.h header file (#4239) (@sy-records) - * Fixed EVENT_HANDSHAKE BC (#4248) (@sy-records) - * Fixed SW_LOCK_CHECK_RETURN (#4302) (@zmyWL) - * Fixed problems with Swoole\Atomic\Long M1 chip (swoole/swoole-src@e6fae2e) (@matyhtf) - * Fixed missing return value of Coroutine\go (swoole/library@1ed49db) (@matyhtf) - * Fixed StringObject consistency between other methods and its immutability (swoole/library#111) (@leocavalcante) - * Fixed StringObject substr error (swoole/library#113) (@sy-records) - - Kernel - --- - * Did not hook disabled functions (#4283) (@twose) - - Test - --- - * Added Cygwin build (#4222) (@sy-records) - * Added alpine 3.13 and 3.14 into building test (#4309) (@limingxinleo) + - 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. - - - - - - - - + + + + + + + + @@ -127,8 +192,7 @@ - - + @@ -140,6 +204,7 @@ + @@ -162,6 +227,7 @@ + @@ -170,26 +236,37 @@ + + + + + + + + + + + + - - + + + + - - - @@ -233,12 +310,14 @@ + + @@ -249,6 +328,7 @@ + @@ -307,10 +387,8 @@ - - @@ -319,54 +397,43 @@ - - - - + - - - + + - + - - - + + - - - - - - + - @@ -392,54 +459,56 @@ + - - - - - + + + - - - - - + - + + + + + + + + @@ -447,12 +516,9 @@ - - - @@ -478,16 +544,16 @@ + - - + @@ -498,22 +564,35 @@ - + + + + + + + + + + + + + + + + + + + - - - - - @@ -527,6 +606,7 @@ + @@ -536,43 +616,131 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - + + + + + + + + - @@ -599,10 +767,12 @@ + + @@ -620,15 +790,37 @@ + - - + + + + + + + + + + + + + + + + + + + + + + + @@ -641,9 +833,12 @@ + + + @@ -670,10 +865,10 @@ - + @@ -694,14 +889,19 @@ + + + + + @@ -714,6 +914,7 @@ + @@ -743,15 +944,17 @@ + + - + @@ -767,6 +970,7 @@ + @@ -796,6 +1000,8 @@ + + @@ -848,10 +1054,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -861,6 +1091,7 @@ + @@ -873,11 +1104,14 @@ + + + @@ -896,8 +1130,12 @@ + + + + @@ -915,6 +1153,7 @@ + @@ -926,6 +1165,11 @@ + + + + + @@ -963,24 +1207,36 @@ + + + + + + + + + + + + @@ -991,6 +1247,14 @@ + + + + + + + + @@ -1036,6 +1300,7 @@ + @@ -1046,9 +1311,8 @@ - - + @@ -1058,6 +1322,7 @@ + @@ -1065,11 +1330,11 @@ - + @@ -1097,7 +1362,10 @@ + + + @@ -1107,6 +1375,7 @@ + @@ -1126,6 +1395,7 @@ + @@ -1145,31 +1415,48 @@ + + + + + + + + + + + + + + + + + + - - - - - - + + + + + @@ -1190,13 +1477,16 @@ + + + @@ -1204,6 +1494,7 @@ + @@ -1226,14 +1517,23 @@ + + + + + + + + + @@ -1245,6 +1545,7 @@ + @@ -1254,6 +1555,7 @@ + @@ -1263,6 +1565,7 @@ + @@ -1296,11 +1599,16 @@ + + + + + @@ -1309,6 +1617,12 @@ + + + + + + @@ -1318,22 +1632,29 @@ + + + + + + + @@ -1343,56 +1664,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1403,9 +1760,11 @@ + + @@ -1414,6 +1773,7 @@ + @@ -1422,101 +1782,168 @@ + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + @@ -1527,83 +1954,51 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + + - + + + @@ -1630,21 +2025,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + @@ -1676,6 +2123,10 @@ + + + + @@ -1692,12 +2143,15 @@ + + + @@ -1705,6 +2159,7 @@ + @@ -1715,6 +2170,7 @@ + @@ -1756,9 +2212,14 @@ + + + + + @@ -1770,15 +2231,18 @@ + - - - + + + + + @@ -1791,8 +2255,6 @@ - - @@ -1820,6 +2282,8 @@ + + @@ -1849,6 +2313,8 @@ + + @@ -1884,9 +2350,17 @@ + + + + + + + + @@ -1921,6 +2395,7 @@ + @@ -1934,6 +2409,7 @@ + @@ -1942,14 +2418,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1962,14 +2491,17 @@ + + + @@ -1977,7 +2509,9 @@ + + @@ -1990,58 +2524,35 @@ - + + + + - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - + + + - - - @@ -2062,6 +2573,7 @@ + @@ -2075,12 +2587,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2093,6 +2641,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2100,10 +2701,11 @@ - + + @@ -2112,25 +2714,14 @@ + - - - - - - - - - - - - - 7.2.0 + 8.1.0 1.4.0 @@ -2139,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 112b5e0077a..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,14 +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 @@ -74,21 +97,13 @@ 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(int code, const char *format, ...); +static void swoole_fatal_error_impl(int code, const char *format, ...); swoole::Logger *sw_logger() { return g_logger_instance; @@ -110,6 +125,24 @@ void *sw_realloc(void *ptr, size_t size) { return SwooleG.std_allocator.realloc(ptr, size); } +static void bug_report_message_init() { + SwooleG.bug_report_message += "\n" + std::string(SWOOLE_BUG_REPORT) + "\n"; + + struct utsname u; + if (uname(&u) != -1) { + SwooleG.bug_report_message += + swoole::std_string::format("OS: %s %s %s %s\n", u.sysname, u.release, u.version, u.machine); + } + +#ifdef __VERSION__ + SwooleG.bug_report_message += swoole::std_string::format("GCC_VERSION: %s\n", __VERSION__); +#endif + +#ifdef SW_USE_OPENSSL + SwooleG.bug_report_message += swoole_ssl_get_version_message(); +#endif +} + void swoole_init(void) { if (SwooleG.init) { return; @@ -120,8 +153,9 @@ void swoole_init(void) { SwooleG.running = 1; SwooleG.init = 1; - SwooleG.std_allocator = { malloc, calloc, realloc, free }; - SwooleG.fatal_error = swoole_fatal_error; + 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)); SwooleG.pagesize = getpagesize(); @@ -150,7 +184,7 @@ void swoole_init(void) { SwooleG.max_sockets = SW_MAX_SOCKETS_DEFAULT; struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0) { - swSysWarn("getrlimit() failed"); + swoole_sys_warning("getrlimit() failed"); } else { SwooleG.max_sockets = SW_MAX((uint32_t) rlmt.rlim_cur, SW_MAX_SOCKETS_DEFAULT); SwooleG.max_sockets = SW_MIN((uint32_t) rlmt.rlim_cur, SW_SESSION_LIST_SIZE); @@ -158,49 +192,37 @@ void swoole_init(void) { SwooleTG.buffer_stack = new swoole::String(SW_STACK_BUFFER_SIZE); - if (!swoole_set_task_tmpdir(SW_TASK_TMP_DIR) ) { + if (!swoole_set_task_tmpdir(SW_TASK_TMP_DIR)) { exit(4); } // init signalfd #ifdef HAVE_SIGNALFD - swSignalfd_init(); - SwooleG.use_signalfd = 1; + swoole_signalfd_init(); SwooleG.enable_signalfd = 1; #endif -} - -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()) { - swWarn("Function '%s' has already been added", name); - return SW_ERR; - } else { - functions.emplace(std::make_pair(_name, func)); - return SW_OK; - } + // init bug report message + bug_report_message_init(); } -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_EXTERN_C_BEGIN -SW_API int swoole_add_hook(enum swGlobal_hook_type type, swHookFunc func, int push_back) { +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 swGlobal_hook_type type, void *arg) { +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; +} + SW_API const char *swoole_version(void) { return SWOOLE_VERSION; } @@ -233,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 = {}; } @@ -242,16 +271,30 @@ 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; } -SW_API void swoole_set_dns_server(const std::string server) { +SW_API void swoole_set_dns_server(const std::string &server) { char *_port; int dns_server_port = SW_DNS_SERVER_PORT; char dns_server_host[32]; strcpy(dns_server_host, server.c_str()); - if ((_port = strchr((char *)server.c_str(), ':'))) { + if ((_port = strchr((char *) server.c_str(), ':'))) { dns_server_port = atoi(_port + 1); if (dns_server_port <= 0 || dns_server_port > 65535) { dns_server_port = SW_DNS_SERVER_PORT; @@ -276,12 +319,12 @@ SW_API std::pair swoole_get_dns_server() { bool swoole_set_task_tmpdir(const std::string &dir) { if (dir.at(0) != '/') { - swWarn("wrong absolute path '%s'", dir.c_str()); + swoole_warning("wrong absolute path '%s'", dir.c_str()); return false; } if (access(dir.c_str(), R_OK) < 0 && !swoole_mkdir_recursive(dir)) { - swWarn("create task tmp dir(%s) failed", dir.c_str()); + swoole_warning("create task tmp dir(%s) failed", dir.c_str()); return false; } @@ -289,21 +332,36 @@ bool swoole_set_task_tmpdir(const std::string &dir) { SwooleG.task_tmpfile = sw_tg_buffer()->to_std_string(); if (SwooleG.task_tmpfile.length() >= SW_TASK_TMP_PATH_SIZE) { - swWarn("task tmp_dir is too large, the max size is '%d'", SW_TASK_TMP_PATH_SIZE - 1); + swoole_warning("task tmp_dir is too large, the max size is '%d'", SW_TASK_TMP_PATH_SIZE - 1); return false; } 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()) { - swFatalError(SW_ERROR_OPERATION_NOT_SUPPORT, "must be forked outside the coroutine"); + swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, "must be forked outside the coroutine"); } if (SwooleTG.async_threads) { - swTrace("aio_task_num=%d, reactor=%p", SwooleTG.async_threads->task_num, sw_reactor()); - swFatalError(SW_ERROR_OPERATION_NOT_SUPPORT, "can not create server after using async file operation"); + 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"); } } if (flags & SW_FORK_PRECHECK) { @@ -333,7 +391,7 @@ pid_t swoole_fork(int flags) { // reset eventLoop if (swoole_event_is_available()) { swoole_event_free(); - swTraceLog(SW_TRACE_REACTOR, "reactor has been destroyed"); + swoole_trace_log(SW_TRACE_REACTOR, "reactor has been destroyed"); } } else { /** @@ -344,13 +402,30 @@ pid_t swoole_fork(int flags) { /** * reset signal handler */ - swSignal_clear(); + 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]); @@ -384,7 +459,6 @@ void swoole_dump_hex(const char *data, size_t outlen) { } printf("\n"); } -#endif /** * Recursive directory creation @@ -395,7 +469,7 @@ bool swoole_mkdir_recursive(const std::string &dir) { // PATH_MAX limit includes string trailing null character if (len + 1 > PATH_MAX) { - swWarn("mkdir(%s) failed. Path exceeds the limit of %d characters", dir.c_str(), PATH_MAX - 1); + swoole_warning("mkdir(%s) failed. Path exceeds the limit of %d characters", dir.c_str(), PATH_MAX - 1); return false; } swoole_strlcpy(tmp, dir.c_str(), PATH_MAX); @@ -410,7 +484,7 @@ bool swoole_mkdir_recursive(const std::string &dir) { tmp[i] = 0; if (access(tmp, R_OK) != 0) { if (mkdir(tmp, 0755) == -1) { - swSysWarn("mkdir(%s) failed", tmp); + swoole_sys_warning("mkdir(%s) failed", tmp); return -1; } } @@ -491,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) { @@ -523,7 +597,7 @@ int swoole_system_random(int min, int max) { bytes_to_read = sizeof(random_value); if (read(dev_random_fd, next_random_byte, bytes_to_read) < bytes_to_read) { - swSysWarn("read() from /dev/urandom failed"); + swoole_sys_warning("read() from /dev/urandom failed"); return SW_ERR; } return min + (random_value % (max - min + 1)); @@ -531,10 +605,10 @@ int swoole_system_random(int min, int max) { void swoole_redirect_stdout(int new_fd) { if (dup2(new_fd, STDOUT_FILENO) < 0) { - swSysWarn("dup2(STDOUT_FILENO) failed"); + swoole_sys_warning("dup2(STDOUT_FILENO) failed"); } if (dup2(new_fd, STDERR_FILENO) < 0) { - swSysWarn("dup2(STDERR_FILENO) failed"); + swoole_sys_warning("dup2(STDERR_FILENO) failed"); } } @@ -672,7 +746,7 @@ int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream) { } if ((child_pid = fork()) == -1) { - swSysWarn("fork() failed"); + swoole_sys_warning("fork() failed"); close(fds[0]); close(fds[1]); return SW_ERR; @@ -725,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)]; @@ -738,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; @@ -770,7 +852,7 @@ bool swoole_get_env(const char *name, int *value) { int swoole_get_systemd_listen_fds() { int ret; if (!swoole_get_env("LISTEN_FDS", &ret)) { - swWarn("invalid LISTEN_FDS"); + swoole_warning("invalid LISTEN_FDS"); return -1; } else if (ret >= SW_MAX_LISTEN_PORT) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, "LISTEN_FDS is too big"); @@ -800,12 +882,10 @@ void swoole_print_backtrace(void) { free(stacktrace); } #else -void swoole_print_backtrace(void) { - -} +void swoole_print_backtrace(void) {} #endif -static void swoole_fatal_error(int code, const char *format, ...) { +static void swoole_fatal_error_impl(int code, const char *format, ...) { size_t retval = 0; va_list args; @@ -814,7 +894,18 @@ static void swoole_fatal_error(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 { @@ -822,22 +913,33 @@ namespace swoole { size_t DataHead::dump(char *_buf, size_t _len) { return sw_snprintf(_buf, _len, - "swDataHead[%p]\n" + "DataHead[%p]\n" "{\n" " long fd = %ld;\n" + " uint64_t msg_id = %" PRIu64 ";\n" " uint32_t len = %d;\n" " int16_t reactor_id = %d;\n" " uint8_t type = %d;\n" " uint8_t flags = %d;\n" " uint16_t server_fd = %d;\n" + " uint16_t ext_flags = %d;\n" + " double time = %f;\n" "}\n", this, fd, + msg_id, len, reactor_id, type, flags, - server_fd); + server_fd, + ext_flags, + time); +} + +void DataHead::print() { + sw_tg_buffer()->length = dump(sw_tg_buffer()->str, sw_tg_buffer()->size); + printf("%.*s", (int) sw_tg_buffer()->length, sw_tg_buffer()->str); } std::string dirname(const std::string &file) { @@ -866,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); @@ -876,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 aace9cc5571..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 | +----------------------------------------------------------------------+ */ @@ -47,7 +47,7 @@ Channel *Channel::make(size_t size, size_t maxlen, int flags) { } if (mem == nullptr) { - swWarn("alloc(%ld) failed", size); + swoole_warning("alloc(%ld) failed", size); return nullptr; } @@ -71,7 +71,7 @@ Channel *Channel::make(size_t size, size_t maxlen, int flags) { if (flags & SW_CHAN_NOTIFY) { object->notify_pipe = new Pipe(true); if (!object->notify_pipe->ready()) { - swWarn("notify_fd init failed"); + swoole_warning("notify_fd init failed"); delete object->notify_pipe; return nullptr; } 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 19be32b6085..4fdc154ab28 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -10,14 +10,18 @@ | 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) : code(code) { +Exception::Exception(int code) throw() : code(code) { msg = swoole_strerror(code); } } // namespace swoole @@ -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: @@ -163,6 +188,12 @@ const char *swoole_strerror(int code) { return "Websocket unconnected"; case SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED: 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: @@ -185,6 +216,16 @@ const char *swoole_strerror(int code) { return "Server invalid request"; case SW_ERROR_SERVER_CONNECT_FAIL: 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: @@ -227,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__ @@ -236,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 9afc21983fa..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 | +----------------------------------------------------------------------+ */ @@ -116,33 +116,33 @@ void Logger::set_rotation(int _rotation) { bool Logger::redirect_stdout_and_stderr(int enable) { if (enable) { if (!opened) { - swWarn("no log file opened"); + swoole_warning("no log file opened"); return false; } if (redirected) { - swWarn("has been redirected"); + swoole_warning("has been redirected"); return false; } if ((stdout_fd = dup(STDOUT_FILENO)) < 0) { - swSysWarn("dup(STDOUT_FILENO) failed"); + swoole_sys_warning("dup(STDOUT_FILENO) failed"); return false; } if ((stderr_fd = dup(STDERR_FILENO)) < 0) { - swSysWarn("dup(STDERR_FILENO) failed"); + swoole_sys_warning("dup(STDERR_FILENO) failed"); return false; } swoole_redirect_stdout(log_fd); redirected = true; } else { if (!redirected) { - swWarn("no redirected"); + swoole_warning("no redirected"); return false; } if (dup2(stdout_fd, STDOUT_FILENO) < 0) { - swSysWarn("dup2(STDOUT_FILENO) failed"); + swoole_sys_warning("dup2(STDOUT_FILENO) failed"); } if (dup2(stderr_fd, STDERR_FILENO) < 0) { - swSysWarn("dup2(STDERR_FILENO) failed"); + swoole_sys_warning("dup2(STDERR_FILENO) failed"); } ::close(stdout_fd); ::close(stderr_fd); @@ -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 4c8bd7f4847..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; - swTraceLog(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; - swTraceLog(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) { - swTraceLog(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) { - swTraceLog(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 dc58f44f7e2..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; @@ -77,7 +85,7 @@ bool Timer::init_reactor(Reactor *reactor) { reactor->set_end_callback(Reactor::PRIORITY_TIMER, [this](Reactor *) { select(); }); reactor->set_exit_condition(Reactor::EXIT_CONDITION_TIMER, - [this](Reactor *reactor, int &event_num) -> bool { return count() == 0; }); + [this](Reactor *reactor, size_t &event_num) -> bool { return count() == 0; }); reactor->add_destroy_callback([](void *) { if (swoole_timer_is_available()) { @@ -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_; } @@ -141,13 +149,13 @@ TimerNode *Timer::add(long _msec, bool persistent, void *data, const TimerCallba return nullptr; } map.emplace(std::make_pair(tnode->id, tnode)); - swTraceLog(SW_TRACE_TIMER, - "id=%ld, exec_msec=%" PRId64 ", msec=%ld, round=%" PRIu64 ", exist=%lu", - tnode->id, - tnode->exec_msec, - _msec, - tnode->round, - count()); + swoole_trace_log(SW_TRACE_TIMER, + "id=%ld, exec_msec=%" PRId64 ", msec=%ld, round=%" PRIu64 ", exist=%lu", + tnode->id, + tnode->exec_msec, + _msec, + tnode->round, + count()); return tnode; } @@ -157,12 +165,12 @@ bool Timer::remove(TimerNode *tnode) { } if (sw_unlikely(_current_id > 0 && tnode->id == _current_id)) { tnode->removed = true; - swTraceLog(SW_TRACE_TIMER, - "set-remove: id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", - tnode->id, - tnode->exec_msec, - tnode->round, - count()); + swoole_trace_log(SW_TRACE_TIMER, + "set-remove: id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", + tnode->id, + tnode->exec_msec, + tnode->round, + count()); return true; } if (sw_unlikely(!map.erase(tnode->id))) { @@ -174,12 +182,12 @@ bool Timer::remove(TimerNode *tnode) { if (tnode->destructor) { tnode->destructor(tnode); } - swTraceLog(SW_TRACE_TIMER, - "id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", - tnode->id, - tnode->exec_msec, - tnode->round, - count()); + swoole_trace_log(SW_TRACE_TIMER, + "id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", + tnode->id, + tnode->exec_msec, + tnode->round, + count()); delete tnode; return true; } @@ -193,7 +201,7 @@ int Timer::select() { TimerNode *tnode = nullptr; HeapNode *tmp; - swTraceLog(SW_TRACE_TIMER, "timer msec=%" PRId64 ", round=%" PRId64, now_msec, round); + swoole_trace_log(SW_TRACE_TIMER, "timer msec=%" PRId64 ", round=%" PRId64, now_msec, round); while ((tmp = heap.top())) { tnode = (TimerNode *) tmp->data; @@ -203,12 +211,12 @@ int Timer::select() { _current_id = tnode->id; if (!tnode->removed) { - swTraceLog(SW_TRACE_TIMER, - "id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", - tnode->id, - tnode->exec_msec, - tnode->round, - count() - 1); + swoole_trace_log(SW_TRACE_TIMER, + "id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%lu", + tnode->id, + tnode->exec_msec, + tnode->round, + count() - 1); tnode->callback(this, tnode); } _current_id = -1; @@ -218,6 +226,7 @@ int Timer::select() { while (tnode->exec_msec <= now_msec) { tnode->exec_msec += tnode->interval; } + tnode->exec_count++; heap.change_priority(tnode->exec_msec, tmp); continue; } @@ -225,39 +234,40 @@ int Timer::select() { heap.pop(); map.erase(tnode->id); delete tnode; + tnode = nullptr; } if (!tnode || !tmp) { next_msec_ = -1; set(this, -1); } else { - long next_msec = tnode->exec_msec - now_msec; - if (next_msec <= 0) { - next_msec = 1; + next_msec_ = tnode->exec_msec - now_msec; + if (next_msec_ <= 0) { + next_msec_ = 1; } - set(this, next_msec); + set(this, next_msec_); } round++; 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) { - swSysWarn("clock_gettime(CLOCK_MONOTONIC) failed"); + swoole_sys_warning("clock_gettime(CLOCK_MONOTONIC) failed"); return SW_ERR; } time->tv_sec = _now.tv_sec; time->tv_usec = _now.tv_nsec / 1000; #else -if (gettimeofday(time, nullptr) < 0) { - swSysWarn("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 499a3ef041e..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,25 +121,8 @@ void Coroutine::resume() { } origin = current; current = this; - ctx.swap_in(); - check_end(); -} -void Coroutine::yield_naked() { - SW_ASSERT(current == this); - state = STATE_WAITING; - current = origin; - ctx.swap_out(); -} - -void Coroutine::resume_naked() { - SW_ASSERT(current != this); - if (sw_unlikely(on_bailout)) { - return; - } - state = STATE_RUNNING; - origin = current; - current = this; + CALC_EXECUTE_USEC(origin, this); ctx.swap_in(); check_end(); } @@ -153,7 +145,7 @@ void Coroutine::close() { on_close(task); } #if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) - swTraceLog( + swoole_trace_log( SW_TRACE_CONTEXT, "coroutine#%ld stack memory use less than %ld bytes", get_cid(), ctx.get_stack_usage()); #endif current = origin; @@ -201,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) { - swError("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 @@ -219,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 83b69ae99ee..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 | +----------------------------------------------------------------------+ */ @@ -31,14 +31,14 @@ void Channel::timer_callback(Timer *timer, TimerNode *tnode) { msg->co->resume(); } -void Channel::yield(enum opcode type) { +void Channel::yield(enum Opcode type) { Coroutine *co = Coroutine::get_current_safe(); if (type == PRODUCER) { producer_queue.push_back(co); - swTraceLog(SW_TRACE_CHANNEL, "producer cid=%ld", co->get_cid()); + swoole_trace_log(SW_TRACE_CHANNEL, "producer cid=%ld", co->get_cid()); } else { consumer_queue.push_back(co); - swTraceLog(SW_TRACE_CHANNEL, "consumer cid=%ld", co->get_cid()); + swoole_trace_log(SW_TRACE_CHANNEL, "consumer cid=%ld", co->get_cid()); } Coroutine::CancelFunc cancel_fn = [this, type](Coroutine *co) { if (type == CONSUMER) { @@ -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); @@ -143,7 +141,7 @@ bool Channel::push(void *data, double timeout) { * push data */ data_queue.push(data); - swTraceLog(SW_TRACE_CHANNEL, "push data to channel, count=%ld", length()); + swoole_trace_log(SW_TRACE_CHANNEL, "push data to channel, count=%ld", length()); /** * notify consumer */ @@ -158,7 +156,7 @@ bool Channel::close() { if (closed) { return false; } - swTraceLog(SW_TRACE_CHANNEL, "channel closed"); + swoole_trace_log(SW_TRACE_CHANNEL, "channel closed"); closed = true; while (!producer_queue.empty()) { Coroutine *co = pop_coroutine(PRODUCER); diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index db8139a4d47..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.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_coroutine_context.h" + #ifdef SW_CONTEXT_PROTECT_STACK_PAGE #include #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) @@ -30,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 @@ -46,17 +47,17 @@ Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) stack_ = (char *) sw_malloc(stack_size_); #endif if (!stack_) { - swFatalError(SW_ERROR_MALLOC_FAIL, "failed to malloc stack memory."); + swoole_fatal_error(SW_ERROR_MALLOC_FAIL, "failed to malloc stack memory."); exit(254); } - swTraceLog(SW_TRACE_COROUTINE, "alloc stack: size=%u, ptr=%p", stack_size_, stack_); + swoole_trace_log(SW_TRACE_COROUTINE, "alloc stack: size=%u, ptr=%p", stack_size_, stack_); void *sp = (void *) ((char *) stack_ + stack_size_); #ifdef USE_VALGRIND 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_); @@ -67,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 @@ -86,7 +87,7 @@ Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) Context::~Context() { if (stack_) { - swTraceLog(SW_TRACE_COROUTINE, "free stack: ptr=%p", stack_); + swoole_trace_log(SW_TRACE_COROUTINE, "free stack: ptr=%p", stack_); #ifdef USE_VALGRIND VALGRIND_STACK_DEREGISTER(valgrind_stack_id); #endif @@ -119,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 eb60a2261aa..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,14 +552,14 @@ 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; } double ori_timeout = socket->get_timeout(event == SW_EVENT_READ ? Socket::TIMEOUT_READ : Socket::TIMEOUT_WRITE); socket->set_timeout(timeout); - bool retval = socket->poll((enum swEvent_type) event); + bool retval = socket->poll((enum swEventType) event); socket->set_timeout(ori_timeout); return retval ? SW_OK : SW_ERR; } @@ -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 20fe3f6ee49..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 | +----------------------------------------------------------------------+ */ @@ -99,7 +99,7 @@ int Socket::error_event_callback(Reactor *reactor, Event *event) { return SW_OK; } -bool Socket::add_event(const enum swEvent_type event) { +bool Socket::add_event(const EventType event) { bool ret = true; if (sw_likely(!(socket->events & event))) { if (socket->removed) { @@ -112,6 +112,31 @@ bool Socket::add_event(const enum swEvent_type 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. @@ -119,12 +144,16 @@ bool Socket::add_event(const enum swEvent_type event) { * We only need to set the errCode for the socket operation when wait_event returns true, * which means that the exception's error code priority is greater than the current event error priority. */ -bool Socket::wait_event(const enum swEvent_type event, const void **__buf, size_t __n) { - enum swEvent_type added_event = event; +bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { + EventType added_event = event; Coroutine *co = Coroutine::get_current_safe(); 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); @@ -144,19 +173,13 @@ bool Socket::wait_event(const enum swEvent_type event, const void **__buf, size_ if (sw_unlikely(!add_event(event))) { return false; } - swTraceLog(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"); + swoole_trace_log(SW_TRACE_SOCKET, + "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; @@ -194,14 +217,12 @@ bool Socket::wait_event(const enum swEvent_type event, const void **__buf, size_ #ifdef SW_USE_OPENSSL want_event = SW_EVENT_NULL; #endif - swTraceLog(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; + swoole_trace_log(SW_TRACE_SOCKET, + "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); - } - - swTraceLog(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; } - swTraceLog(SW_TRACE_HTTP_CLIENT, "proxy response: <str); + 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(enum swSocket_type _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_CORO_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; } @@ -468,7 +416,7 @@ bool Socket::init_sock() { } bool Socket::init_reactor_socket(int _fd) { - socket = swoole::make_socket(_fd, SW_FD_CORO_SOCKET); + socket = swoole::make_socket(_fd, SW_FD_CO_SOCKET); sock_fd = _fd; socket->object = this; socket->socket_type = type; @@ -480,14 +428,14 @@ 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; } init_options(); } -Socket::Socket(enum swSocket_type _type) { +Socket::Socket(SocketType _type) { init_sock_type(_type); if (sw_unlikely(!init_sock())) { return; @@ -495,18 +443,21 @@ Socket::Socket(enum swSocket_type _type) { init_options(); } -Socket::Socket(int _fd, enum swSocket_type _type) { - init_sock_type(_type); +Socket::Socket(int _fd, SocketType _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; } @@ -526,7 +477,7 @@ Socket::Socket(network::Socket *sock, Socket *server_sock) { socket = sock; socket->object = this; socket->socket_type = type; - socket->fd_type = SW_FD_CORO_SOCKET; + socket->fd_type = SW_FD_CO_SOCKET; init_options(); /* inherits server socket options */ dns_timeout = server_sock->dns_timeout; @@ -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(enum swEvent_type 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; @@ -915,9 +895,9 @@ ssize_t Socket::readv_all(network::IOVector *io_vector) { TimerController timer(&read_timer, read_timeout, this, timer_callback); retval = socket->readv(io_vector); - swTraceLog(SW_TRACE_SOCKET, "readv %ld bytes, errno=%d", retval, errno); + 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; @@ -977,9 +957,9 @@ ssize_t Socket::writev_all(network::IOVector *io_vector) { TimerController timer(&write_timer, write_timeout, this, timer_callback); retval = socket->writev(io_vector); - swTraceLog(SW_TRACE_SOCKET, "writev %ld bytes, errno=%d", retval, errno); + 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; } @@ -1146,7 +1128,11 @@ bool Socket::listen(int backlog) { return false; } this->backlog = backlog <= 0 ? SW_BACKLOG : backlog; - if (socket->listen(this->backlog) != 0) { + if (socket->listen(this->backlog) < 0) { + set_err(errno); + return false; + } + if (socket->get_name(&socket->info) < 0) { set_err(errno); return false; } @@ -1160,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); @@ -1175,7 +1166,7 @@ Socket *Socket::accept(double timeout) { Socket *client_sock = new Socket(conn, this); if (sw_unlikely(client_sock->get_fd() < 0)) { - swSysWarn("new Socket() failed"); + swoole_sys_warning("new Socket() failed"); set_err(errno); delete client_sock; return nullptr; @@ -1185,23 +1176,19 @@ 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; ssl_context->protocols = SW_SSL_DTLS; socket->chunk_size = SW_SSL_BUFFER_SIZE; #else - swWarn("DTLS support require openssl-1.1 or later"); + swoole_warning("DTLS support require openssl-1.1 or later"); return false; #endif } ssl_context->http_v2 = http2; if (!ssl_context->create()) { - swWarn("swSSL_get_context() error"); return false; } socket->ssl_send_ = 1; @@ -1235,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) { @@ -1257,7 +1252,7 @@ bool Socket::ssl_handshake() { } } } else { - enum swReturn_code retval; + ReturnCode retval; TimerController timer(&read_timer, read_timeout, this, timer_callback); do { @@ -1295,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(); @@ -1326,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; @@ -1417,8 +1413,8 @@ ssize_t Socket::sendto(const std::string &host, int port, const void *__buf, siz TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = ::sendto(sock_fd, __buf, __n, 0, (struct sockaddr *) &addr, addr_size); - swTraceLog(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() && + swoole_trace_log(SW_TRACE_SOCKET, "sendto %ld/%ld bytes, errno=%d", retval, __n, errno); + } 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); } @@ -1442,9 +1438,9 @@ ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, sockle TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = ::recvfrom(sock_fd, __buf, __n, 0, _addr, _socklen); - swTraceLog(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)))); + swoole_trace_log(SW_TRACE_SOCKET, "recvfrom %ld/%ld bytes, errno=%d", retval, __n, errno); + } 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; } @@ -1452,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) { @@ -1473,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); - swTraceLog(SW_TRACE_SOCKET, "packet_len=%ld, length=%ld", packet_len, 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; } @@ -1670,7 +1669,7 @@ bool Socket::ssl_shutdown() { } #endif -bool Socket::cancel(const enum swEvent_type event) { +bool Socket::cancel(const EventType event) { if (!has_bound(event)) { return false; } @@ -1683,41 +1682,39 @@ bool Socket::cancel(const enum swEvent_type 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; } } @@ -1730,9 +1727,6 @@ bool Socket::close() { * 3. called close() and it return false but it will not be accessed anywhere else */ Socket::~Socket() { - if (socket == nullptr) { - return; - } #ifdef SW_DEBUG if (SwooleG.running) { SW_ASSERT(!has_bound() && socket->removed); @@ -1744,16 +1738,19 @@ Socket::~Socket() { if (write_buffer) { delete write_buffer; } + if (socks5_proxy) { + delete socks5_proxy; + } + if (http_proxy) { + delete http_proxy; + } + if (socket == nullptr) { + return; + } /* {{{ release socket resources */ #ifdef SW_USE_OPENSSL ssl_shutdown(); #endif - if (socket->in_buffer) { - delete socket->in_buffer; - } - if (socket->out_buffer) { - delete socket->out_buffer; - } if (sock_domain == AF_UNIX && !bind_address.empty()) { ::unlink(bind_address_info.addr.un.sun_path); bind_address_info = {}; @@ -1761,29 +1758,11 @@ Socket::~Socket() { if (socket->socket_type == SW_SOCK_UNIX_DGRAM) { ::unlink(socket->info.addr.un.sun_path); } - if (socks5_proxy) { - delete socks5_proxy; - } - if (http_proxy) { - delete http_proxy; + 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 11ff4e20fcb..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; @@ -43,12 +49,10 @@ void System::clear_dns_cache() { } static void sleep_callback(Coroutine *co, bool *canceled) { - bool _canceled = *canceled; - delete canceled; - if (_canceled) { - return; + if (*canceled == false) { + co->resume(); } - co->resume(); + delete canceled; } int System::sleep(double sec) { @@ -88,11 +92,11 @@ std::shared_ptr System::read_file(const char *file, bool lock) { async([&result, file, lock]() { File fp(file, O_RDONLY); if (!fp.ready()) { - swSysWarn("open(%s, O_RDONLY) failed", file); + swoole_sys_warning("open(%s, O_RDONLY) failed", file); return; } if (lock && !fp.lock(LOCK_SH)) { - swSysWarn("flock(%s, LOCK_SH) failed", file); + swoole_sys_warning("flock(%s, LOCK_SH) failed", file); return; } ssize_t filesize = fp.get_size(); @@ -105,7 +109,7 @@ std::shared_ptr System::read_file(const char *file, bool lock) { result = fp.read_content(); } if (lock && !fp.unlock()) { - swSysWarn("flock(%s, LOCK_UN) failed", file); + swoole_sys_warning("flock(%s, LOCK_UN) failed", file); } }); return result; @@ -117,19 +121,19 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock async([&]() { File _file(file, file_flags, 0644); if (!_file.ready()) { - swSysWarn("open(%s, %d) failed", file, file_flags); + swoole_sys_warning("open(%s, %d) failed", file, file_flags); return; } if (lock && !_file.lock(LOCK_EX)) { - swSysWarn("flock(%s, LOCK_EX) failed", file); + swoole_sys_warning("flock(%s, LOCK_EX) failed", file); return; } size_t bytes = _file.write_all(buf, length); if ((file_flags & SW_AIO_WRITE_FSYNC) && !_file.sync()) { - swSysWarn("fsync(%s) failed", file); + swoole_sys_warning("fsync(%s) failed", file); } if (lock && !_file.unlock()) { - swSysWarn("flock(%s, LOCK_UN) failed", file); + swoole_sys_warning("flock(%s, LOCK_UN) failed", file); } retval = bytes; }); @@ -138,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); @@ -164,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; } } @@ -214,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, int &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); + [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } - /* always enable signalfd */ - SwooleG.use_signalfd = SwooleG.enable_signalfd = 1; - swSignal_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++; - swSignal_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 { @@ -359,7 +345,7 @@ static void socket_poll_completed(void *data) { task->co->resume(); } -static inline void socket_poll_trigger_event(Reactor *reactor, CoroPollTask *task, int fd, enum swEvent_type event) { +static inline void socket_poll_trigger_event(Reactor *reactor, CoroPollTask *task, int fd, EventType event) { auto i = task->fds->find(fd); if (event == SW_EVENT_ERROR && !(i->second.events & SW_EVENT_ERROR)) { if (i->second.events & SW_EVENT_READ) { @@ -397,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) { @@ -410,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) { @@ -431,7 +417,7 @@ bool System::socket_poll(std::unordered_map &fds, double timeou if (timeout == 0) { struct pollfd *event_list = (struct pollfd *) sw_calloc(fds.size(), sizeof(struct pollfd)); if (!event_list) { - swWarn("calloc() failed"); + swoole_warning("calloc() failed"); return false; } int n = 0; @@ -457,7 +443,7 @@ bool System::socket_poll(std::unordered_map &fds, double timeou task.co = Coroutine::get_current_safe(); for (auto i = fds.begin(); i != fds.end(); i++) { - i->second.socket = swoole::make_socket(i->first, SW_FD_CORO_POLL); + i->second.socket = swoole::make_socket(i->first, SW_FD_CO_POLL); if (swoole_event_add(i->second.socket, i->second.events) < 0) { i->second.socket->free(); continue; @@ -471,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(); @@ -488,7 +474,7 @@ struct EventWaiter { EventWaiter(int fd, int events, double timeout) { error_ = revents = 0; - socket = swoole::make_socket(fd, SW_FD_CORO_EVENT); + socket = swoole::make_socket(fd, SW_FD_CO_EVENT); socket->object = this; timer = nullptr; co = Coroutine::get_current_safe(); @@ -508,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); @@ -534,7 +521,7 @@ struct EventWaiter { } }; -static inline void event_waiter_callback(Reactor *reactor, EventWaiter *waiter, enum swEvent_type event) { +static inline void event_waiter_callback(Reactor *reactor, EventWaiter *waiter, EventType event) { if (waiter->revents == 0) { reactor->defer([waiter](void *data) { waiter->co->resume(); }); } @@ -602,16 +589,48 @@ 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_CORO_POLL | SW_EVENT_READ, socket_poll_read_callback); - reactor->set_handler(SW_FD_CORO_POLL | SW_EVENT_WRITE, socket_poll_write_callback); - reactor->set_handler(SW_FD_CORO_POLL | SW_EVENT_ERROR, socket_poll_error_callback); + 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); + reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_ERROR, socket_poll_error_callback); - reactor->set_handler(SW_FD_CORO_EVENT | SW_EVENT_READ, event_waiter_read_callback); - reactor->set_handler(SW_FD_CORO_EVENT | SW_EVENT_WRITE, event_waiter_write_callback); - reactor->set_handler(SW_FD_CORO_EVENT | SW_EVENT_ERROR, event_waiter_error_callback); + reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_READ, event_waiter_read_callback); + reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_WRITE, event_waiter_write_callback); + 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) { @@ -644,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; } @@ -663,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}; @@ -683,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 32c4448013a..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) { @@ -43,7 +45,7 @@ Mutex::Mutex(int flags) : Lock() { #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED pthread_mutexattr_setpshared(&impl->attr_, PTHREAD_PROCESS_SHARED); #else - swWarn("PTHREAD_MUTEX_PSHARED is not supported"); + swoole_warning("PTHREAD_MUTEX_PSHARED is not supported"); #endif } @@ -51,22 +53,24 @@ Mutex::Mutex(int flags) : Lock() { #ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST pthread_mutexattr_setrobust(&impl->attr_, PTHREAD_MUTEX_ROBUST); #else - swWarn("PTHREAD_MUTEX_ROBUST is not supported"); + swoole_warning("PTHREAD_MUTEX_ROBUST is not supported"); #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 40520ced0d5..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,10 +72,10 @@ 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; - swTraceLog(SW_TRACE_BUFFER, "chunk_n=%lu|size=%u|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); + swoole_trace_log(SW_TRACE_BUFFER, "chunk_n=%lu|size=%u|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); _pos += _n; _length -= _n; @@ -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,11 +121,11 @@ 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; - swTraceLog(SW_TRACE_BUFFER, "chunk_n=%lu|size=%lu|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); + swoole_trace_log(SW_TRACE_BUFFER, "chunk_n=%lu|size=%lu|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); chunk->length += _n; iov_remain_len -= _n; diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index 66471dd5b1b..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]; }; @@ -51,6 +51,9 @@ struct FixedPoolImpl { * create new FixedPool, random alloc/free fixed size memory */ FixedPool::FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared) { + if (slice_num < 2) { + throw Exception(SW_ERROR_INVALID_PARAMS); + } slice_size = SW_MEM_ALIGNED_SIZE(slice_size); size_t size = slice_num * (sizeof(FixedPoolSlice) + slice_size); size_t alloc_size = size + sizeof(*impl); @@ -76,14 +79,17 @@ 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; impl->size = size - sizeof(*impl); - impl->slice_num = impl->size / (slice_size + sizeof(FixedPoolSlice)); + uint32_t slice_num = impl->size / (slice_size + sizeof(FixedPoolSlice)); + if (slice_num < 2) { + throw Exception(SW_ERROR_INVALID_PARAMS); + } + impl->slice_num = slice_num; impl->memory = memory; impl->allocated = false; impl->init(); @@ -109,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; @@ -119,74 +125,76 @@ 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; } } while (1); } -void *FixedPool::alloc(uint32_t size) { - FixedPoolSlice *slice; +uint32_t FixedPool::get_number_of_spare_slice() { + return impl->slice_num - impl->slice_use; +} - 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 { +uint32_t FixedPool::get_number_of_total_slice() { + return impl->slice_num; +} + +uint32_t FixedPool::get_slice_size() { + return impl->slice_size; +} + +void *FixedPool::alloc(uint32_t size) { + 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; } @@ -201,7 +209,7 @@ FixedPool::~FixedPool() { } } -void FixedPool::debug() { +void FixedPool::debug(int max_lines) { int line = 0; FixedPoolSlice *slice = impl->head; @@ -212,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 0d237528ff4..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) { @@ -84,7 +84,7 @@ void *GlobalMemory::alloc(uint32_t size) { std::unique_lock lock(impl->lock); if (alloc_size > impl->pagesize) { - swWarn("failed to alloc %d bytes, exceed the maximum size[%d]", size, impl->pagesize); + swoole_warning("failed to alloc %d bytes, exceed the maximum size[%d]", size, impl->pagesize); return nullptr; } @@ -93,12 +93,12 @@ void *GlobalMemory::alloc(uint32_t size) { impl = new GlobalMemoryImpl(old_impl->pagesize, old_impl->shared); } - swTrace("alloc_size=%u, size=%u", alloc_size, size); + swoole_trace("alloc_size=%u, size=%u", alloc_size, size); if (impl->alloc_offset + alloc_size > impl->pagesize) { char *page = impl->new_page(); if (page == nullptr) { - swWarn("alloc memory error"); + swoole_warning("alloc memory error"); return nullptr; } } @@ -124,6 +124,10 @@ size_t GlobalMemory::capacity() { return impl->pagesize - impl->alloc_offset; } +size_t GlobalMemory::get_memory_size() { + return impl->pagesize * impl->pages.size(); +} + GlobalMemory::~GlobalMemory() { delete impl; } diff --git a/src/memory/ring_buffer.cc b/src/memory/ring_buffer.cc index f0da0e8f94a..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 | +----------------------------------------------------------------------+ */ @@ -69,7 +69,7 @@ RingBuffer::RingBuffer(uint32_t size, bool shared) { impl->shared = shared; impl->memory = mem; - swDebug("memory: ptr=%p", mem); + swoole_debug("memory: ptr=%p", mem); } void RingBufferImpl::collect() { @@ -134,7 +134,7 @@ void *RingBuffer::alloc(uint32_t size) { impl->alloc_offset += alloc_size; impl->alloc_count++; - swDebug("alloc: ptr=%p", (void *) (item->data - (char *) impl->memory)); + swoole_debug("alloc: ptr=%p", (void *) (item->data - (char *) impl->memory)); return item->data; } @@ -147,12 +147,12 @@ void RingBuffer::free(void *ptr) { assert(item->lock == 1); if (item->lock != 1) { - swDebug("invalid free: index=%d, ptr=%p", item->index, (void *) (item->data - (char *) impl->memory)); + swoole_debug("invalid free: index=%d, ptr=%p", item->index, (void *) (item->data - (char *) impl->memory)); } else { item->lock = 0; } - swDebug("free: ptr=%p", (void *) (item->data - (char *) impl->memory)); + swoole_debug("free: ptr=%p", (void *) (item->data - (char *) impl->memory)); sw_atomic_t *free_count = &impl->free_count; sw_atomic_fetch_add(free_count, 1); diff --git a/src/memory/shared_memory.cc b/src/memory/shared_memory.cc index 00757df87c7..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 | +----------------------------------------------------------------------+ */ @@ -62,7 +62,7 @@ void *SharedMemory::alloc(size_t size) { if (!mem) #endif { - swSysWarn("mmap(%lu) failed", size); + swoole_sys_warning("mmap(%lu) failed", size); return nullptr; } else { object.size_ = size; @@ -75,7 +75,7 @@ void SharedMemory::free(void *ptr) { SharedMemory *object = SharedMemory::fetch_object(ptr); size_t size = object->size_; if (munmap(object, size) < 0) { - swSysWarn("munmap(%p, %lu) failed", object, size); + swoole_sys_warning("munmap(%p, %lu) failed", object, size); } } diff --git a/src/memory/table.cc b/src/memory/table.cc index e0683a6b303..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) { - swWarn("unknown column type"); + 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,19 +96,32 @@ size_t Table::get_memory_size() { */ _memory_size += size * sizeof(TableRow *); - memory_size = _memory_size; - - swTrace("_memory_size=%lu, _row_num=%lu, _row_memory_size=%lu", _memory_size, _row_num, _row_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(); + unlock(); + return num; +} + +uint32_t Table::get_total_slice_num() { + return pool->get_number_of_total_slice(); +} + bool Table::create() { if (created) { 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); @@ -142,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; @@ -163,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); @@ -203,7 +203,7 @@ void TableRow::lock() { */ if (kill(lock_pid, 0) < 0 && errno == ESRCH) { *lock = 1; - swWarn("lock process[%d] not exists, force unlock", lock_pid); + swoole_warning("lock process[%d] not exists, force unlock", lock_pid); goto _success; } /** @@ -219,7 +219,7 @@ void TableRow::lock() { */ else if ((swoole::time(true) - t) > SW_TABLE_FORCE_UNLOCK_TIME) { *lock = 1; - swWarn("timeout, force unlock"); + swoole_warning("timeout, force unlock"); goto _success; } sw_yield(); @@ -297,24 +297,18 @@ TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int * row->lock(); int _out_flags = 0; -#ifdef SW_TABLE_DEBUG - int _conflict_level = 0; -#endif + uint32_t _conflict_level = 1; if (row->active) { for (;;) { 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); -#ifdef SW_TABLE_DEBUG conflict_count++; if (_conflict_level > conflict_max_level) { conflict_max_level = _conflict_level; } -#endif - unlock(); + TableRow *new_row = alloc_row(); if (!new_row) { return nullptr; } @@ -326,9 +320,7 @@ TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int * } else { row = row->next; _out_flags |= SW_TABLE_FLAG_CONFLICT; -#ifdef SW_TABLE_DEBUG _conflict_level++; -#endif } } } else { @@ -340,6 +332,12 @@ TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int * *out_flags = _out_flags; } + if (_out_flags & SW_TABLE_FLAG_NEW_ROW) { + sw_atomic_fetch_add(&(insert_count), 1); + } else { + sw_atomic_fetch_add(&(update_count), 1); + } + return row; } @@ -387,17 +385,14 @@ 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: + sw_atomic_fetch_add(&(delete_count), 1); sw_atomic_fetch_sub(&(row_num), 1); row->unlock(); @@ -426,7 +421,7 @@ void TableRow::set_value(TableColumn *col, void *value, size_t vlen) { break; default: if (vlen > (col->size - sizeof(TableStringLength))) { - swWarn("[key=%s,field=%s]string value is too long", key, col->name.c_str()); + swoole_warning("[key=%s,field=%s]string value is too long", key, col->name.c_str()); vlen = col->size - sizeof(TableStringLength); } if (value == nullptr) { diff --git a/src/network/address.cc b/src/network/address.cc index 9a760183e80..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 { @@ -46,7 +48,7 @@ int Address::get_port() { } } -bool Address::assign(enum swSocket_type _type, const std::string &_host, int _port) { +bool Address::assign(SocketType _type, const std::string &_host, int _port) { type = _type; const char *host = _host.c_str(); if (_type == SW_SOCK_TCP || _type == SW_SOCK_UDP) { @@ -74,5 +76,40 @@ bool Address::assign(enum swSocket_type _type, const std::string &_host, int _po 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 31988e58276..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) { @@ -68,11 +68,11 @@ void Client::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_STREAM_CLIENT | SW_EVENT_ERROR, Client_onError); } -Client::Client(enum swSocket_type _type, bool _async) : async(_async) { +Client::Client(SocketType _type, bool _async) : async(_async) { fd_type = Socket::is_stream(_type) ? SW_FD_STREAM_CLIENT : SW_FD_DGRAM_CLIENT; socket = swoole::make_socket(_type, fd_type, (async ? SW_SOCK_NONBLOCK : 0) | SW_SOCK_CLOEXEC); if (socket == nullptr) { - swSysWarn("socket() failed"); + swoole_sys_warning("socket() failed"); return; } @@ -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; } @@ -283,7 +284,7 @@ int Client::enable_ssl_encrypt() { } #else { - swWarn("DTLS support require openssl-1.1 or later"); + swoole_warning("DTLS support require openssl-1.1 or later"); return SW_ERR; } #endif @@ -427,14 +428,6 @@ Client::~Client() { if (server_str) { ::sw_free((void *) server_str); } - if (socket->out_buffer) { - delete socket->out_buffer; - socket->out_buffer = nullptr; - } - if (socket->in_buffer) { - delete socket->in_buffer; - socket->in_buffer = nullptr; - } if (socks5_proxy) { delete socks5_proxy; } @@ -565,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); @@ -587,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 @@ -609,7 +616,7 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou } if (!(cli->onConnect && cli->onError && cli->onClose && cli->onReceive)) { - swWarn("onConnect/onError/onReceive/onClose callback have not set"); + swoole_warning("onConnect/onError/onReceive/onClose callback have not set"); return SW_ERR; } @@ -623,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) { - swWarn("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; @@ -669,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 { @@ -757,7 +747,12 @@ static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, in while (1) { #ifdef HAVE_KQUEUE int timeout_ms = (int) (cli->timeout * 1000); - if (cli->socket->wait_event(timeout_ms, SW_EVENT_READ) < 0) { +#ifdef SW_USE_OPENSSL + if (cli->socket->ssl) { + timeout_ms = 0; + } +#endif + if (timeout_ms > 0 && cli->socket->wait_event(timeout_ms, SW_EVENT_READ) < 0) { return -1; } #endif @@ -776,7 +771,7 @@ static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, in } } #ifdef SW_USE_OPENSSL - if (errno == EAGAIN && 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; @@ -797,7 +792,7 @@ static int Client_udp_connect(Client *cli, const char *host, int port, double ti } if (cli->async && !cli->onReceive) { - swWarn("onReceive callback have not set"); + swoole_warning("onReceive callback have not set"); return SW_ERR; } @@ -816,7 +811,7 @@ static int Client_udp_connect(Client *cli, const char *host, int port, double ti unlink(client_addr->sun_path); if (bind(cli->socket->fd, (struct sockaddr *) client_addr, sizeof(cli->socket->info.addr.un)) < 0) { - swSysWarn("bind(%s) failed", client_addr->sun_path); + swoole_sys_warning("bind(%s) failed", client_addr->sun_path); return SW_ERR; } } @@ -830,7 +825,7 @@ static int Client_udp_connect(Client *cli, const char *host, int port, double ti } #else { - swWarn("DTLS support require openssl-1.1 or later"); + swoole_warning("DTLS support require openssl-1.1 or later"); return SW_ERR; } #endif @@ -899,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; } @@ -952,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; } @@ -1055,14 +992,11 @@ 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: - swSysWarn("Read from socket[%d] failed", event->fd); + swoole_sys_warning("Read from socket[%d] failed", event->fd); return SW_OK; case SW_CLOSE: goto __close; @@ -1076,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; @@ -1131,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; @@ -1149,7 +1075,6 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - sw_free(event->buf); } static int Client_onWrite(Reactor *reactor, Event *event) { @@ -1186,7 +1111,7 @@ static int Client_onWrite(Reactor *reactor, Event *event) { ret = _socket->get_option(SOL_SOCKET, SO_ERROR, &err); swoole_set_last_error(err); if (ret < 0) { - swSysWarn("getsockopt(%d) failed", event->fd); + swoole_sys_warning("getsockopt(%d) failed", event->fd); return SW_ERR; } @@ -1199,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 d234bee6d5d..22ac3ab9dd8 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -10,28 +10,38 @@ | 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 #include #include +#include +#include +#include + +#define SW_PATH_HOSTS "/etc/hosts" #ifdef SW_USE_CARES #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] = {}; if ((fp = fopen(SwooleG.dns_resolvconf_path.c_str(), "rt")) == nullptr) { - swSysWarn("fopen(%s) failed", SwooleG.dns_resolvconf_path.c_str()); + swoole_sys_warning("fopen(%s) failed", SwooleG.dns_resolvconf_path.c_str()); return false; } @@ -51,16 +61,75 @@ bool swoole_load_resolv_conf() { return true; } +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 { -enum swDNS_type { +enum RecordType { SW_DNS_A_RECORD = 0x01, // Lookup IPv4 address SW_DNS_AAAA_RECORD = 0x1c, // Lookup IPv6 address SW_DNS_MX_RECORD = 0x0f // Lookup mail server for domain }; -enum swDNS_error { +enum DNSError { SW_DNS_NOT_EXIST, // Error: address does not exist SW_DNS_TIMEOUT, // Lookup time expired SW_DNS_ERROR // No memory or other error @@ -103,6 +172,55 @@ static int domain_encode(const char *src, int n, char *dest); static void domain_decode(char *str); static std::string parse_ip_address(void *vaddr, int type); +std::string get_ip_by_hosts(const std::string &search_domain) { + std::ifstream file(SwooleG.dns_hosts_path.empty() ? SW_PATH_HOSTS : SwooleG.dns_hosts_path); + if (!file.is_open()) { + return ""; + } + + std::string line; + std::string domain; + std::string txtaddr; + std::vector domains; + std::unordered_map result{}; + + while (getline(file, line)) { + std::string::size_type ops = line.find_first_of('#'); + if (ops != std::string::npos) { + line[ops] = '\0'; + } + + if (line[0] == '\n' || line[0] == '\0' || line[0] == '\r') { + continue; + } + + std::istringstream stream(line); + while (stream >> domain) { + domains.push_back(domain); + } + if (domains.empty() || domains.size() == 1) { + domains.clear(); + continue; + } + + txtaddr = domains[0]; + for (size_t i = 1; i < domains.size(); i++) { + result.insert(std::make_pair(domains[i], txtaddr)); + } + + auto iter = result.find(search_domain); + if (iter != result.end()) { + return iter->second; + } else { + result.clear(); + domains.clear(); + continue; + } + } + + return ""; +} + static std::string parse_ip_address(void *vaddr, int type) { auto addr = reinterpret_cast(vaddr); std::string ip_addr; @@ -160,7 +278,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam int len = strlen(domain); if (domain_encode(domain, len, _domain_name) < 0) { - swWarn("invalid domain[%s]", domain); + swoole_warning("invalid domain[%s]", domain); return result; } @@ -199,7 +317,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam auto ret = _sock.recv(packet, sizeof(packet) - 1); if (ret <= 0) { - swoole_set_last_error(_sock.errCode == ECANCELED ? SW_ERROR_CO_CANCELED: SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + swoole_set_last_error(_sock.errCode == ECANCELED ? SW_ERROR_CO_CANCELED : SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return result; } @@ -226,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 { @@ -255,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 { @@ -343,6 +461,7 @@ struct ResolvContext { int error; bool completed; Coroutine *co; + std::shared_ptr defer_task_cancelled; std::unordered_map sockets; std::vector result; }; @@ -352,13 +471,13 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami ares_library_init(ARES_LIB_INIT_ALL); swoole_event_set_handler(SW_FD_CARES | SW_EVENT_READ, [](Reactor *reactor, Event *event) -> int { auto ctx = reinterpret_cast(event->socket->object); - swTraceLog(SW_TRACE_CARES, "[event callback] readable event, fd=%d", event->socket->fd); + swoole_trace_log(SW_TRACE_CARES, "[event callback] readable event, fd=%d", event->socket->fd); ares_process_fd(ctx->channel, event->fd, ARES_SOCKET_BAD); return SW_OK; }); swoole_event_set_handler(SW_FD_CARES | SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int { auto ctx = reinterpret_cast(event->socket->object); - swTraceLog(SW_TRACE_CARES, "[event callback] writable event, fd=%d", event->socket->fd); + swoole_trace_log(SW_TRACE_CARES, "[event callback] writable event, fd=%d", event->socket->fd); ares_process_fd(ctx->channel, ARES_SOCKET_BAD, event->fd); return SW_OK; }); @@ -369,6 +488,7 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami Coroutine *co = Coroutine::get_current_safe(); ctx.co = co; ctx.completed = false; + ctx.defer_task_cancelled = std::make_shared(false); char lookups[] = "fb"; int res; ctx.ares_opts.lookups = lookups; @@ -385,12 +505,12 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami events |= SW_EVENT_WRITE; } - swTraceLog(SW_TRACE_CARES, "[sock_state_cb], fd=%d, readable=%d, writable=%d", fd, readable, writable); + swoole_trace_log(SW_TRACE_CARES, "[sock_state_cb], fd=%d, readable=%d, writable=%d", fd, readable, writable); network::Socket *_socket = nullptr; if (ctx->sockets.find(fd) == ctx->sockets.end()) { if (events == 0) { - swWarn("error events, fd=%d", fd); + swoole_warning("error events, fd=%d", fd); return; } _socket = make_socket(fd, SW_FD_CARES); @@ -399,7 +519,7 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami } else { _socket = ctx->sockets[fd]; if (events == 0) { - swTraceLog(SW_TRACE_CARES, "[del event], fd=%d", fd); + swoole_trace_log(SW_TRACE_CARES, "[del event], fd=%d", fd); swoole_event_del(_socket); _socket->fd = -1; _socket->free(); @@ -410,16 +530,16 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami if (_socket->events) { swoole_event_set(_socket, events); - swTraceLog(SW_TRACE_CARES, "[set event] fd=%d, events=%d", fd, events); + swoole_trace_log(SW_TRACE_CARES, "[set event] fd=%d, events=%d", fd, events); } else { swoole_event_add(_socket, events); - swTraceLog(SW_TRACE_CARES, "[add event] fd=%d, events=%d", fd, events); + swoole_trace_log(SW_TRACE_CARES, "[add event] fd=%d, events=%d", fd, events); } }; ctx.ares_flags = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB | ARES_OPT_LOOKUPS; if ((res = ares_init_options(&ctx.channel, &ctx.ares_opts, ctx.ares_flags)) != ARES_SUCCESS) { - swWarn("ares_init_options() failed, Error: %s[%d]", ares_strerror(res), res); + swoole_warning("ares_init_options() failed, Error: %s[%d]", ares_strerror(res), res); goto _return; } @@ -439,10 +559,10 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami inet_pton(AF_INET, SwooleG.dns_server_host.c_str(), &servers.addr.addr4); ares_set_servers(ctx.channel, &servers); if (SwooleG.dns_server_port != SW_DNS_SERVER_PORT) { - swWarn("not support to set port of dns server"); + swoole_warning("not support to set port of dns server"); } #else - swWarn("not support to set dns server"); + swoole_warning("not support to set dns server"); #endif } @@ -453,7 +573,7 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami [](void *data, int status, int timeouts, struct hostent *hostent) { auto ctx = reinterpret_cast(data); - swTraceLog(SW_TRACE_CARES, "[cares callback] status=%d, timeouts=%d", status, timeouts); + swoole_trace_log(SW_TRACE_CARES, "[cares callback] status=%d, timeouts=%d", status, timeouts); if (timeouts > 0) { ctx->error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; @@ -474,8 +594,12 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami } _resume: if (ctx->co && ctx->co->is_suspending()) { + auto _cancelled = ctx->defer_task_cancelled; swoole_event_defer( - [](void *data) { + [_cancelled](void *data) { + if (*_cancelled) { + return; + } Coroutine *co = reinterpret_cast(data); co->resume(); }, @@ -498,7 +622,7 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami ares_process_fd(ctx.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); ctx.error = ARES_ETIMEOUT; } else { - swTraceLog(SW_TRACE_CARES, "lookup success, result_count=%lu", ctx.result.size()); + swoole_trace_log(SW_TRACE_CARES, "lookup success, result_count=%lu", ctx.result.size()); } _destroy: if (ctx.error) { @@ -514,6 +638,7 @@ std::vector dns_lookup_impl_with_cares(const char *domain, int fami break; } } + *ctx.defer_task_cancelled = true; ares_destroy(ctx.channel); _return: return ctx.result; @@ -642,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: - swWarn("unknown socket family[%d]", ptr->ai_family); + 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; @@ -680,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) { @@ -693,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 f0183d19554..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; @@ -89,7 +89,7 @@ int Socket::sendfile_blocking(const char *filename, off_t offset, size_t length, File file(filename, O_RDONLY); if (!file.ready()) { - swSysWarn("open(%s) failed", filename); + swoole_sys_warning("open(%s) failed", filename); return SW_ERR; } @@ -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) { - swSysWarn("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,11 +124,11 @@ 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 { - swSysWarn("send %lu bytes failed", iov[1].iov_len); + swoole_sys_warning("send %lu bytes failed", iov[1].iov_len); return SW_ERR; } } 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) { - swSysWarn("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,11 +201,11 @@ 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 { - swSysWarn("send %lu bytes failed", __len); + swoole_sys_warning("send %lu bytes failed", __len); return SW_ERR; } } @@ -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; @@ -308,7 +311,7 @@ ssize_t Socket::recvfrom_blocking(char *__buf, size_t __len, int flags, Address static void socket_free_defer(void *ptr) { Socket *sock = (Socket *) ptr; if (sock->fd != -1 && close(sock->fd) != 0) { - swSysWarn("close(%d) failed", sock->fd); + swoole_sys_warning("close(%d) failed", sock->fd); } delete sock; } @@ -320,6 +323,12 @@ void Socket::free() { if (send_timer) { swoole_timer_del(send_timer); } + if (in_buffer) { + delete in_buffer; + } + if (out_buffer) { + delete out_buffer; + } if (swoole_event_is_available()) { removed = 1; swoole_event_defer(socket_free_defer, this); @@ -335,12 +344,12 @@ int Socket::bind(const std::string &_host, int *port) { const char *host = _host.c_str(); if (set_reuse_addr() < 0) { - swSysWarn("setsockopt(%d, SO_REUSEADDR) failed", fd); + swoole_sys_warning("setsockopt(%d, SO_REUSEADDR) failed", fd); } // UnixSocket if (socket_type == SW_SOCK_UNIX_DGRAM || socket_type == SW_SOCK_UNIX_STREAM) { if (l_host == 0 || l_host > sizeof(address.addr.un) - 1) { - swWarn("bad unix socket file"); + swoole_warning("bad unix socket file"); errno = EINVAL; return SW_ERR; } @@ -355,7 +364,7 @@ int Socket::bind(const std::string &_host, int *port) { host = "::"; } if (inet_pton(AF_INET6, host, &address.addr.inet_v6.sin6_addr) < 0) { - swSysWarn("inet_pton(AF_INET6, %s) failed", host); + swoole_sys_warning("inet_pton(AF_INET6, %s) failed", host); return SW_ERR; } address.addr.inet_v6.sin6_port = htons(*port); @@ -374,7 +383,7 @@ int Socket::bind(const std::string &_host, int *port) { host = "0.0.0.0"; } if (inet_pton(AF_INET, host, &address.addr.inet_v4.sin_addr) < 0) { - swSysWarn("inet_pton(AF_INET, %s) failed", host); + swoole_sys_warning("inet_pton(AF_INET, %s) failed", host); return SW_ERR; } address.addr.inet_v4.sin_port = htons(*port); @@ -411,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) { - swSysWarn("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; @@ -419,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) { - swSysWarn("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; @@ -429,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; @@ -436,7 +452,7 @@ static bool _set_timeout(int fd, int type, double timeout) { timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); ret = setsockopt(fd, SOL_SOCKET, type, (void *) &timeo, sizeof(timeo)); if (ret < 0) { - swSysWarn("setsockopt(SO_SNDTIMEO, %s) failed", type == SO_SNDTIMEO ? "SEND" : "RECV"); + swoole_sys_warning("setsockopt(SO_SNDTIMEO, %s) failed", type == SO_SNDTIMEO ? "SEND" : "RECV"); return false; } else { return true; @@ -452,7 +468,7 @@ static bool _fcntl_set_option(int sock, int nonblock, int cloexec) { } while (opts < 0 && errno == EINTR); if (opts < 0) { - swSysWarn("fcntl(%d, GETFL) failed", sock); + swoole_sys_warning("fcntl(%d, GETFL) failed", sock); } if (nonblock) { @@ -466,7 +482,7 @@ static bool _fcntl_set_option(int sock, int nonblock, int cloexec) { } while (ret < 0 && errno == EINTR); if (ret < 0) { - swSysWarn("fcntl(%d, SETFL, opts) failed", sock); + swoole_sys_warning("fcntl(%d, SETFL, opts) failed", sock); return false; } } @@ -478,7 +494,7 @@ static bool _fcntl_set_option(int sock, int nonblock, int cloexec) { } while (opts < 0 && errno == EINTR); if (opts < 0) { - swSysWarn("fcntl(%d, GETFL) failed", sock); + swoole_sys_warning("fcntl(%d, GETFL) failed", sock); } if (cloexec) { @@ -492,7 +508,7 @@ static bool _fcntl_set_option(int sock, int nonblock, int cloexec) { } while (ret < 0 && errno == EINTR); if (ret < 0) { - swSysWarn("fcntl(%d, SETFD, opts) failed", sock); + swoole_sys_warning("fcntl(%d, SETFD, opts) failed", sock); return false; } } @@ -533,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(); @@ -551,12 +567,13 @@ int Socket::handle_sendfile() { ret = ::swoole_sendfile(fd, task->file.get_fd(), &task->offset, sendn); } - swTrace("ret=%d|task->offset=%ld|sendn=%lu|filesize=%lu", ret, (long) task->offset, sendn, task->length); + 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: - swSysWarn("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: @@ -596,11 +613,11 @@ 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: - swSysWarn("send to fd[%d] failed", fd); + swoole_sys_warning("send to fd[%d] failed", fd); break; case SW_CLOSE: close_wait = 1; @@ -628,25 +645,25 @@ 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; } int Socket::sendfile(const char *filename, off_t offset, size_t length) { std::unique_ptr task(new SendfileRequest(filename, offset, length)); if (!task->file.ready()) { - swSysWarn("open(%s) failed", filename); + swoole_sys_warning("open(%s) failed", filename); return SW_OK; } FileStatus file_stat; if (!task->file.stat(&file_stat)) { - swSysWarn("fstat(%s) failed", filename); + swoole_sys_warning("fstat(%s) failed", filename); return SW_ERR; } if (file_stat.st_size == 0) { - swWarn("empty file[%s]", filename); + swoole_warning("empty file[%s]", filename); return SW_ERR; } @@ -668,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; @@ -710,11 +727,11 @@ 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; } - swTraceLog(SW_TRACE_SOCKET, "recv %ld/%ld bytes, errno=%d", total_bytes, __n, errno); + swoole_trace_log(SW_TRACE_SOCKET, "recv %ld/%ld bytes, errno=%d", total_bytes, __n, errno); return total_bytes; } @@ -740,7 +757,7 @@ ssize_t Socket::send(const void *__buf, size_t __n, int __flags) { } } - swTraceLog(SW_TRACE_SOCKET, "send %ld/%ld bytes, errno=%d", retval, __n, errno); + swoole_trace_log(SW_TRACE_SOCKET, "send %ld/%ld bytes, errno=%d", retval, __n, errno); return retval; } @@ -753,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; @@ -775,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); @@ -804,7 +842,7 @@ ssize_t Socket::peek(void *__buf, size_t __n, int __flags) { } } while (retval < 0 && errno == EINTR); - swTraceLog(SW_TRACE_SOCKET, "peek %ld/%ld bytes, errno=%d", retval, __n, errno); + swoole_trace_log(SW_TRACE_SOCKET, "peek %ld/%ld bytes, errno=%d", retval, __n, errno); return retval; } @@ -816,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); @@ -855,7 +893,7 @@ bool Socket::ssl_check_host(const char *tls_host_name) { #ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT /* X509_check_host() is only available in OpenSSL 1.0.2+ */ if (X509_check_host(cert, tls_host_name, strlen(tls_host_name), 0, nullptr) != 1) { - swWarn("X509_check_host(): no match"); + swoole_warning("X509_check_host(): no match"); goto _failed; } goto _found; @@ -884,16 +922,16 @@ bool Socket::ssl_check_host(const char *tls_host_name) { } str = altname->d.dNSName; - swTrace("SSL subjectAltName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); + swoole_trace("SSL subjectAltName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); if (ssl_check_name(tls_host_name, str) == SW_OK) { - swTrace("SSL subjectAltName: match"); + swoole_trace("SSL subjectAltName: match"); GENERAL_NAMES_free(altnames); goto _found; } } - swTrace("SSL subjectAltName: no match"); + swoole_trace("SSL subjectAltName: no match"); GENERAL_NAMES_free(altnames); goto _failed; } @@ -920,14 +958,14 @@ bool Socket::ssl_check_host(const char *tls_host_name) { entry = X509_NAME_get_entry(sname, i); str = X509_NAME_ENTRY_get_data(entry); - swTrace("SSL commonName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); + swoole_trace("SSL commonName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); if (ssl_check_name(tls_host_name, str) == SW_OK) { - swTrace("SSL commonName: match"); + swoole_trace("SSL commonName: match"); goto _found; } } - swTrace("SSL commonName: no match"); + swoole_trace("SSL commonName: no match"); #endif _failed: @@ -972,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; } @@ -987,18 +1025,18 @@ static int _ssl_read_x509_file(X509 *cert, char *buffer, size_t length) { }; if (bio == nullptr) { - swWarn("BIO_new() failed"); + swoole_warning("BIO_new() failed"); return -1; } if (PEM_write_bio_X509(bio, cert) == 0) { - swWarn("PEM_write_bio_X509() failed"); + swoole_warning("PEM_write_bio_X509() failed"); return -1; } len = BIO_pending(bio); if (len < 0 && len > (long) length) { - swWarn("certificate length[%ld] is too big", len); + swoole_warning("certificate length[%ld] is too big", len); return -1; } return BIO_read(bio, buffer, len); @@ -1011,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); @@ -1051,7 +1095,7 @@ const char *Socket::ssl_get_error_reason(int *reason) { return ERR_reason_error_string(error); } -enum swReturn_code Socket::ssl_accept() { +ReturnCode Socket::ssl_accept() { ssl_clear_error(); int n = SSL_accept(ssl); @@ -1088,7 +1132,7 @@ enum swReturn_code Socket::ssl_accept() { } else if (err == SSL_ERROR_SSL) { int reason; const char *error_string = ssl_get_error_reason(&reason); - swWarn( + swoole_warning( "bad SSL client[%s:%d], reason=%d, error_string=%s", info.get_ip(), info.get_port(), reason, error_string); return SW_ERROR; } else if (err == SSL_ERROR_SYSCALL) { @@ -1100,7 +1144,7 @@ enum swReturn_code Socket::ssl_accept() { #endif return SW_ERROR; } - swWarn("SSL_do_handshake() failed. Error: %s[%ld|%d]", strerror(errno), err, errno); + swoole_warning("SSL_do_handshake() failed. Error: %s[%ld|%d]", strerror(errno), err, errno); return SW_ERROR; } @@ -1114,7 +1158,7 @@ int Socket::ssl_connect() { #ifdef SW_LOG_TRACE_OPEN const char *ssl_version = SSL_get_version(ssl); const char *ssl_cipher = SSL_get_cipher_name(ssl); - swTraceLog(SW_TRACE_SSL, "connected (%s %s)", ssl_version, ssl_cipher); + swoole_trace_log(SW_TRACE_SSL, "connected (%s %s)", ssl_version, ssl_cipher); #endif return SW_OK; @@ -1132,7 +1176,7 @@ int Socket::ssl_connect() { ssl_state = SW_SSL_STATE_WAIT_STREAM; return SW_OK; } else if (err == SSL_ERROR_ZERO_RETURN) { - swDebug("SSL_connect(fd=%d) closed", fd); + swoole_debug("SSL_connect(fd=%d) closed", fd); return SW_ERR; } else if (err == SSL_ERROR_SYSCALL) { if (n) { @@ -1143,13 +1187,18 @@ int Socket::ssl_connect() { long err_code = ERR_get_error(); char *msg = ERR_error_string(err_code, sw_tg_buffer()->str); - swNotice("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; @@ -1157,16 +1206,16 @@ 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) { - swSysWarn("write() failed"); + if (catch_write_error(errno) == SW_ERROR) { + swoole_sys_warning("write() failed"); } } else { *_offset += ret; } - swTraceLog(SW_TRACE_REACTOR, "fd=%d, readn=%ld, n=%ld, ret=%ld", fd, readn, n, ret); + swoole_trace_log(SW_TRACE_REACTOR, "fd=%d, readn=%ld, n=%ld, ret=%ld", fd, readn, n, ret); return ret; } else { - swSysWarn("pread() failed"); + swoole_sys_warning("pread() failed"); return SW_ERR; } } @@ -1191,19 +1240,19 @@ bool Socket::ssl_shutdown() { int n = SSL_shutdown(ssl); ssl_closed_ = 1; - swTrace("SSL_shutdown: %d", n); + swoole_trace("SSL_shutdown: %d", n); int sslerr = 0; /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ if (n != 1 && ERR_peek_error()) { sslerr = SSL_get_error(ssl, n); - swTrace("SSL_get_error: %d", sslerr); + swoole_trace("SSL_get_error: %d", sslerr); } if (!(n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN)) { int reason; const char *error_string = ssl_get_error_reason(&reason); - swWarn("SSL_shutdown() failed, reason=%d, error_string=%s", reason, error_string); + swoole_warning("SSL_shutdown() failed, reason=%d, error_string=%s", reason, error_string); return false; } @@ -1320,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(); @@ -1403,12 +1451,12 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { ssl = SSL_new(ssl_context->get_context()); if (ssl == nullptr) { - swWarn("SSL_new() failed"); + swoole_warning("SSL_new() failed"); return SW_ERR; } if (!SSL_set_fd(ssl, fd)) { long err = ERR_get_error(); - swWarn("SSL_set_fd() failed. Error: %s[%ld]", ERR_reason_error_string(err), err); + swoole_warning("SSL_set_fd() failed. Error: %s[%ld]", ERR_reason_error_string(err), err); return SW_ERR; } if (_flags & SW_SSL_CLIENT) { @@ -1416,10 +1464,15 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { } else if (_flags & SW_SSL_SERVER) { SSL_set_accept_state(ssl); } - if (SSL_set_ex_data(ssl, swSSL_get_ex_connection_index(), this) == 0) { - swWarn("SSL_set_ex_data() failed"); + if (SSL_set_ex_data(ssl, swoole_ssl_get_ex_connection_index(), this) == 0) { + 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; } @@ -1430,16 +1483,35 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { using network::Socket; -Socket *make_socket(enum swSocket_type type, enum swFd_type fd_type, int flags) { +Socket *make_socket(SocketType type, FdType fd_type, int flags) { int sock_domain; int sock_type; if (Socket::get_domain_and_type(type, &sock_domain, &sock_type) < 0) { - swWarn("unknown socket type [%d]", type); + 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; @@ -1451,33 +1523,29 @@ Socket *make_socket(enum swSocket_type type, enum swFd_type 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(enum swSocket_type type, const char *address, int port, int backlog) { +Socket *make_server_socket(SocketType type, const char *address, int port, int backlog) { Socket *sock = swoole::make_socket(type, SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC); if (sock == nullptr) { - swSysWarn("socket() failed"); + swoole_sys_warning("socket() failed"); return nullptr; } if (sock->bind(address, &port) < 0) { @@ -1485,14 +1553,14 @@ Socket *make_server_socket(enum swSocket_type type, const char *address, int por return nullptr; } if (sock->is_stream() && sock->listen(backlog) < 0) { - swSysWarn("listen(%s:%d, %d) failed", address, port, backlog); + swoole_sys_warning("listen(%s:%d, %d) failed", address, port, backlog); sock->free(); return nullptr; } return sock; } -Socket *make_socket(int fd, enum swFd_type fd_type) { +Socket *make_socket(int fd, FdType fd_type) { Socket *socket = new Socket(); socket->fd = fd; socket->fd_type = fd_type; diff --git a/src/network/stream.cc b/src/network/stream.cc index 7b84a747193..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 | +----------------------------------------------------------------------+ */ @@ -74,7 +74,7 @@ static void Stream_onClose(Client *cli) { cli); } -Stream::Stream(const char *dst_host, int dst_port, enum swSocket_type type) : client(type, true) { +Stream::Stream(const char *dst_host, int dst_port, SocketType type) : client(type, true) { if (client.socket == nullptr) { return; } @@ -89,7 +89,7 @@ Stream::Stream(const char *dst_host, int dst_port, enum swSocket_type type) : cl set_protocol(&client.protocol); if (client.connect(&client, dst_host, dst_port, -1, 0) < 0) { - swSysWarn("failed to connect to [%s:%d]", dst_host, dst_port); + swoole_sys_warning("failed to connect to [%s:%d]", dst_host, dst_port); return; } connected = true; @@ -106,8 +106,8 @@ Stream::~Stream() { */ void Stream::set_protocol(Protocol *protocol) { protocol->get_package_length = Protocol::default_length_func; - protocol->package_length_size = 4; protocol->package_length_type = 'N'; + protocol->package_length_size = swoole_type_size(protocol->package_length_type); protocol->package_body_offset = 4; protocol->package_length_offset = 0; } @@ -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 7994ccc1544..8fca0974b9b 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 | +----------------------------------------------------------------------+ */ @@ -32,6 +32,9 @@ #include #include +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; + namespace swoole { namespace async { //------------------------------------------------------------------------------- @@ -82,6 +85,10 @@ class ThreadPool { shutdown(); } + bool is_running() { + return running; + } + bool start() { running = true; current_task_id = 0; @@ -110,16 +117,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; /** @@ -128,11 +133,11 @@ class ThreadPool { if (threads.size() + n > worker_num) { n = worker_num - threads.size(); } - swTraceLog(SW_TRACE_AIO, - "Create %zu thread due to wait %fs, we will have %zu threads", - n, - _max_wait_time, - threads.size() + n); + 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); while (n--) { create_thread(); } @@ -141,26 +146,24 @@ 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(); - swDebug("push and notify one: %f", microtime()); + swoole_debug("push and notify one: %f", microtime()); return _event_copy; } - inline size_t worker_count() { + inline size_t get_worker_num() { return threads.size(); } - inline size_t queue_count() { + inline size_t get_queue_size() { std::unique_lock lock(event_mutex); return _queue.count(); } @@ -174,14 +177,14 @@ class ThreadPool { void release_thread(std::thread::id tid) { auto i = threads.find(tid); if (i == threads.end()) { - swWarn("AIO thread#%s is missing", get_thread_id(tid).c_str()); + swoole_warning("AIO thread#%s is missing", get_thread_id(tid).c_str()); return; } else { std::thread *_thread = i->second; - swTraceLog(SW_TRACE_AIO, - "release idle thread#%s, we have %zu now", - get_thread_id(tid).c_str(), - threads.size() - 1); + swoole_trace_log(SW_TRACE_AIO, + "release idle thread#%s, we have %zu now", + get_thread_id(tid).c_str(), + threads.size() - 1); if (_thread->joinable()) { _thread->join(); } @@ -204,6 +207,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,105 +225,102 @@ 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; - }; - - swSignal_none(); - - while (running) { - event_mutex.lock(); - AsyncEvent *event = _queue.pop(); - event_mutex.unlock(); - - swDebug("%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(); - swTraceLog(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; - swSysWarn("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) { - swSysNotice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); + swoole_sys_notice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); return; } } @@ -339,14 +340,10 @@ 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) { - swSysWarn("read() aio events failed"); + swoole_sys_warning("read() aio events failed"); return SW_ERR; } for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++) { @@ -361,8 +358,12 @@ int AsyncThreads::callback(Reactor *reactor, Event *event) { return SW_OK; } -size_t AsyncThreads::thread_count() { - return pool ? pool->worker_count() : 0; +size_t AsyncThreads::get_worker_num() { + return pool ? pool->get_worker_num() : 0; +} + +size_t AsyncThreads::get_queue_size() { + return pool ? pool->get_queue_size() : 0; } void AsyncThreads::notify_one() { @@ -373,7 +374,7 @@ void AsyncThreads::notify_one() { AsyncThreads::AsyncThreads() { if (!SwooleTG.reactor) { - swWarn("no event loop, cannot initialized"); + swoole_warning("no event loop, cannot initialized"); throw swoole::Exception(SW_ERROR_WRONG_OPERATION); } @@ -400,27 +401,39 @@ AsyncThreads::AsyncThreads() { SwooleTG.async_threads = nullptr; }); - sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_AIO_TASK, [](Reactor *reactor, int &event_num) -> bool { + sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_AIO_TASK, [](Reactor *reactor, size_t &event_num) -> bool { if (SwooleTG.async_threads && SwooleTG.async_threads->task_num == 0) { event_num--; } 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 ed237cf367c..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 | +----------------------------------------------------------------------+ */ @@ -27,20 +27,20 @@ int swoole_daemon(int nochdir, int noclose) { pid_t pid; if (!nochdir && chdir("/") != 0) { - swSysWarn("chdir() failed"); + swoole_sys_warning("chdir() failed"); return -1; } if (!noclose) { int fd = open("/dev/null", O_RDWR); if (fd < 0) { - swSysWarn("open() failed"); + swoole_sys_warning("open() failed"); return -1; } if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) { close(fd); - swSysWarn("dup2() failed"); + swoole_sys_warning("dup2() failed"); return -1; } @@ -49,14 +49,14 @@ int swoole_daemon(int nochdir, int noclose) { pid = swoole_fork(SW_FORK_DAEMON); if (pid < 0) { - swSysWarn("fork() failed"); + swoole_sys_warning("fork() failed"); return -1; } if (pid > 0) { _exit(0); } if (setsid() < 0) { - swSysWarn("setsid() failed"); + swoole_sys_warning("setsid() failed"); return -1; } return 0; @@ -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 0ce290e1b07..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 | +----------------------------------------------------------------------+ */ @@ -24,7 +24,7 @@ int swoole_tmpfile(char *filename) { #endif if (tmp_fd < 0) { - swSysWarn("mkstemp(%s) failed", filename); + swoole_sys_warning("mkstemp(%s) failed", filename); return SW_ERR; } else { return tmp_fd; @@ -63,7 +63,7 @@ ssize_t file_get_size(int fd) { std::shared_ptr file_get_contents(const std::string &filename) { File fp(filename, O_RDONLY); if (!fp.ready()) { - swSysWarn("open(%s) failed", filename.c_str()); + swoole_sys_warning("open(%s) failed", filename.c_str()); return nullptr; } @@ -107,12 +107,16 @@ bool file_put_contents(const std::string &filename, const char *content, size_t } File file(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (!file.ready()) { - swSysWarn("open(%s) failed", filename.c_str()); + swoole_sys_warning("open(%s) failed", filename.c_str()); return false; } 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) { @@ -130,7 +134,7 @@ size_t File::write_all(const void *data, size_t len) { if (errno == EINTR) { continue; } else if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - swSysWarn("pwrite(%d, %p, %lu, %lu) failed", fd_, data, len - written_bytes, written_bytes); + swoole_sys_warning("pwrite(%d, %p, %lu, %lu) failed", fd_, data, len - written_bytes, written_bytes); } break; } @@ -150,7 +154,7 @@ size_t File::read_all(void *buf, size_t len) { if (errno == EINTR) { continue; } else if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - swSysWarn("pread(%d, %p, %lu, %lu) failed", fd_, buf, len - read_bytes, read_bytes); + swoole_sys_warning("pread(%d, %p, %lu, %lu) failed", fd_, buf, len - read_bytes, read_bytes); } break; } @@ -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 561dbe1b327..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 | +----------------------------------------------------------------------+ */ @@ -24,7 +24,7 @@ namespace swoole { bool MsgQueue::destroy() { if (msgctl(msg_id_, IPC_RMID, 0) < 0) { - swSysWarn("msgctl(%d, IPC_RMID) failed", msg_id_); + swoole_sys_warning("msgctl(%d, IPC_RMID) failed", msg_id_); return false; } msg_id_ = -1; @@ -49,7 +49,7 @@ MsgQueue::MsgQueue(key_t msg_key, bool blocking, int perms) { blocking_ = blocking; msg_id_ = msgget(msg_key, IPC_CREAT | perms); if (msg_id_ < 0) { - swSysWarn("msgget() failed"); + swoole_sys_warning("msgget() failed"); } else { set_blocking(blocking); } @@ -67,7 +67,7 @@ ssize_t MsgQueue::pop(QueueNode *data, size_t mdata_size) { if (ret < 0) { swoole_set_last_error(errno); if (errno != ENOMSG && errno != EINTR) { - swSysWarn("msgrcv(%d, %zu, %ld) failed", msg_id_, mdata_size, data->mtype); + swoole_sys_warning("msgrcv(%d, %zu, %ld) failed", msg_id_, mdata_size, data->mtype); } } return ret; @@ -82,11 +82,10 @@ bool MsgQueue::push(QueueNode *in, size_t mdata_length) { continue; } if (errno != EAGAIN) { - swSysWarn("msgsnd(%d, %lu, %ld) failed", msg_id_, mdata_length, in->mtype); + swoole_sys_warning("msgsnd(%d, %lu, %ld) failed", msg_id_, mdata_length, in->mtype); } swoole_set_last_error(errno); break; - } return false; } @@ -113,7 +112,7 @@ bool MsgQueue::set_capacity(size_t queue_bytes) { } __stat.msg_qbytes = queue_bytes; if (msgctl(msg_id_, IPC_SET, &__stat)) { - swSysWarn("msgctl(msqid=%d, IPC_SET, msg_qbytes=%lu) failed", msg_id_, queue_bytes); + swoole_sys_warning("msgctl(msqid=%d, IPC_SET, msg_qbytes=%lu) failed", msg_id_, queue_bytes); return false; } return true; diff --git a/src/os/pipe.cc b/src/os/pipe.cc index a31c64c3fa3..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 | +----------------------------------------------------------------------+ */ @@ -40,7 +40,7 @@ bool SocketPair::init_socket(int master_fd, int worker_fd) { Pipe::Pipe(bool _blocking) : SocketPair(_blocking) { if (pipe(socks) < 0) { - swSysWarn("pipe() failed"); + swoole_sys_warning("pipe() failed"); return; } if (!init_socket(socks[1], socks[0])) { @@ -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; } @@ -89,12 +89,12 @@ bool SocketPair::close(int which) { } SocketPair::~SocketPair() { - if (!master_socket) { + if (master_socket) { close(SW_PIPE_CLOSE_MASTER); } - if (!worker_socket) { + if (worker_socket) { close(SW_PIPE_CLOSE_WORKER); } } -} +} // namespace swoole diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index aecee9f8871..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; @@ -52,9 +56,10 @@ void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { continue; } if (swoole_kill(reload_worker_pid, SIGKILL) < 0) { - swSysWarn("failed to force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); + swoole_sys_warning( + "failed to force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); } else { - swWarn("force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); + swoole_warning("force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); } } } @@ -62,22 +67,22 @@ void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { pool->reload_worker_i = 0; pool->reload_init = false; } + /** * Process manager */ -int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPC_type _ipc_mode) { +int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ipc_mode) { worker_num = _worker_num; /** * Shared memory is used here */ workers = (Worker *) sw_mem_pool()->alloc(_worker_num * sizeof(Worker)); if (workers == nullptr) { - swSysWarn("malloc[1] failed"); + swoole_sys_warning("malloc[1] failed"); return SW_ERR; } - message_box = Channel::make(65536, sizeof(WorkerStopMessage), SW_CHAN_LOCK | SW_CHAN_SHM); - if (message_box == nullptr) { + if (create_message_box(SW_MESSAGE_BOX_SIZE) < 0) { return SW_ERR; } @@ -113,11 +118,11 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPC_type _i } 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; @@ -126,9 +131,55 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPC_type _i return SW_OK; } +int ProcessPool::create_message_box(size_t memory_size) { + message_box = Channel::make(memory_size, sizeof(EventData), SW_CHAN_LOCK | SW_CHAN_SHM); + if (message_box == nullptr) { + return SW_ERR; + } + 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) { - swWarn("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); @@ -145,7 +196,7 @@ int ProcessPool::listen(const char *socket_file, int blacklog) { int ProcessPool::listen(const char *host, int port, int blacklog) { if (ipc_mode != SW_IPC_SOCKET) { - swWarn("ipc_mode is not SW_IPC_SOCKET"); + swoole_warning("ipc_mode is not SW_IPC_SOCKET"); return SW_ERR; } stream_info_->socket_file = sw_strdup(host); @@ -160,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)) { - swWarn("must first listen to an tcp port"); + 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; } @@ -189,7 +281,8 @@ int ProcessPool::start() { } int ProcessPool::schedule() { - if (dispatch_mode == SW_DISPATCH_QUEUE) { + // schedule by system message queue + if (schedule_by_sysvmsg) { return 0; } @@ -198,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; } @@ -218,11 +311,37 @@ int ProcessPool::response(const char *data, int length) { return stream_info_->response_buffer->append(data, length); } -/** - * dispatch data to worker - */ -int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { - int ret = 0; +int ProcessPool::push_message(EventData *msg) { + if (message_box->push(msg, msg->size()) < 0) { + return SW_ERR; + } + return swoole_kill(master_pid, SIGIO); +} + +int ProcessPool::push_message(uint8_t type, const void *data, size_t length) { + if (!message_box) { + return SW_ERR; + } + + EventData msg; + assert(length < sizeof(msg.data)); + + msg.info = {}; + msg.info.type = type; + msg.info.len = length; + memcpy(msg.data, data, length); + + return push_message(&msg); +} + +int ProcessPool::pop_message(void *data, size_t size) { + if (!message_box) { + return SW_ERR; + } + return message_box->pop(data, size); +} + +swResultCode ProcessPool::dispatch(EventData *data, int *dst_worker_id) { Worker *worker; if (use_socket) { @@ -231,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; @@ -246,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 { - swWarn("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); @@ -279,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) { @@ -298,14 +406,49 @@ 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) { - swWarn("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 SW_OK; +} + +bool ProcessPool::reload() { + if (reloading) { + return false; + } + reloading = true; + reload_count++; + reload_last_time = ::time(NULL); + 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); } - return ret > 0 ? SW_OK : SW_ERR; + 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() { @@ -314,18 +457,32 @@ void ProcessPool::shutdown() { 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) { - swSysWarn("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) { - swSysWarn("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; @@ -338,28 +495,30 @@ 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; case -1: - swSysWarn("fork() failed"); + swoole_sys_warning("fork() failed"); break; // parent default: @@ -375,107 +534,90 @@ 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 */ out.buf.info.server_fd = worker->id; - if (pool->dispatch_mode == SW_DISPATCH_QUEUE) { + if (pool->schedule_by_sysvmsg) { out.mtype = 0; } else { 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) { - swSysWarn("[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 { - swSysWarn("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) { - swSysWarn("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + 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))) { - swWarn("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", - n, - out.buf.info.len + sizeof(out.buf.info)); - continue; + 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)); + } + 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)); } - - /** - * do task - */ - worker->status = SW_WORKER_BUSY; - ret = pool->onTask(pool, &out.buf); - worker->status = SW_WORKER_IDLE; - if (pool->use_socket && pool->stream_info_->last_connection) { int _end = 0; pool->stream_info_->last_connection->send_blocking((void *) &_end, sizeof(_end)); @@ -483,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) { - swSysWarn("[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 { - swSysWarn("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_) { @@ -552,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) { - swSysWarn("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + 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; @@ -590,37 +778,89 @@ 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) { + if (!running || !message_box) { return false; } WorkerStopMessage msg; msg.pid = getpid(); - msg.worker_id = SwooleG.process_id; + msg.worker_id = swoole_get_process_id(); - if (message_box && message_box->push(&msg, sizeof(msg)) < 0) { - return false; - } - if (swoole_kill(master_pid, SIGIO) < 0) { + if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { return false; } running = false; @@ -630,37 +870,35 @@ 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) { - WorkerStopMessage msg; - while (message_box->pop(&msg, sizeof(msg)) > 0) { + EventData msg; + while (pop_message(&msg, sizeof(msg)) > 0) { if (!running) { continue; } - Worker *exit_worker = get_worker_by_pid(msg.pid); + if (msg.info.type != SW_WORKER_MESSAGE_STOP && onWorkerMessage) { + onWorkerMessage(this, &msg); + continue; + } + WorkerStopMessage worker_stop_msg; + memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); + Worker *exit_worker = get_worker_by_pid(worker_stop_msg.pid); if (exit_worker == nullptr) { continue; } pid_t new_pid = spawn(exit_worker); if (new_pid < 0) { - swSysWarn("fork worker process failed"); + swoole_sys_warning("fork worker process failed"); return SW_ERR; } - map_->erase(msg.pid); + map_->erase(worker_stop_msg.pid); } read_message = false; } @@ -670,12 +908,11 @@ int ProcessPool::wait() { } if (!reloading) { if (errno > 0 && errno != EINTR) { - swSysWarn("[Manager] wait failed"); + swoole_sys_warning("wait() failed"); } continue; } else { if (!reload_init) { - swInfo("reload workers"); reload_init = true; memcpy(reload_workers, workers, sizeof(Worker) * worker_num); if (max_wait_time) { @@ -692,22 +929,20 @@ int ProcessPool::wait() { if (onWorkerNotFound) { onWorkerNotFound(this, exit_status); } else { - swWarn("[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()) { - swWarn("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 ? "\n" SWOOLE_BUG_REPORT : ""); + exit_worker->report_error(exit_status); + if (onWorkerError) { + onWorkerError(this, exit_worker, exit_status); + } } new_pid = spawn(exit_worker); if (new_pid < 0) { - swSysWarn("Fork worker process failed"); + swoole_sys_warning("Fork worker process failed"); return SW_ERR; } map_->erase(exit_status.get_pid()); @@ -725,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; } - swSysWarn("[Manager]swKill(%d) failed", reload_workers[reload_worker_i].pid); + swoole_sys_warning("kill(%d) failed", reload_workers[reload_worker_i].pid); continue; } } @@ -763,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 90a28a8f612..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__ @@ -40,8 +40,8 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { #endif // sent_bytes = (off_t)size; - swTrace( - "send file, ret:%d, out_fd:%d, in_fd:%d, offset:%jd, size:%zu", ret, out_fd, in_fd, (intmax_t) *offset, size); + swoole_trace( + "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; @@ -58,13 +58,13 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { } else if (ret == 0) { return size; } else { - swSysWarn("sendfile failed"); + swoole_sys_warning("sendfile failed"); return SW_ERR; } 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); @@ -72,13 +72,13 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { if (n > 0) { ssize_t ret = write(out_fd, buf, n); if (ret < 0) { - swSysWarn("write() failed"); + swoole_sys_warning("write() failed"); } else { *offset += ret; } return ret; } else { - swSysWarn("pread() failed"); + swoole_sys_warning("pread() failed"); return SW_ERR; } } diff --git a/src/os/signal.cc b/src/os/signal.cc index 1f82a6cf003..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 | +----------------------------------------------------------------------+ */ @@ -23,32 +23,51 @@ #ifdef HAVE_SIGNALFD #include #endif + #ifdef HAVE_KQUEUE +#ifdef USE_KQUEUE_IDE_HELPER +#include "helper/kqueue.h" +#else #include #endif +#endif +#ifdef __NetBSD__ +#include +#endif +using swoole::Event; using swoole::Reactor; +using swoole::Signal; +using swoole::SignalHandler; +using swoole::network::Socket; #ifdef HAVE_SIGNALFD -static swSignalHandler swSignalfd_set(int signo, swSignalHandler handler); -static bool swSignalfd_create(); -static void swSignalfd_clear(); -static int swSignalfd_onSignal(Reactor *reactor, swEvent *event); -#elif HAVE_KQUEUE -static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler handler); +static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler); +static bool swoole_signalfd_create(); +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 +static SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler); #endif -static void swSignal_async_handler(int signo); + +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 swoole::network::Socket *signal_socket = nullptr; +static Socket *signal_socket = nullptr; +static inline bool swoole_signalfd_is_available() { + return signal_fd != -1; +} #endif -static swSignal signals[SW_SIGNO_MAX]; +static Signal signals[SW_SIGNO_MAX]; static int _lock = 0; -char *swSignal_str(int sig) { +char *swoole_signal_to_str(int sig) { static char buf[64]; snprintf(buf, sizeof(buf), "%s", strsignal(sig)); if (strchr(buf, ':') == 0) { @@ -61,19 +80,19 @@ char *swSignal_str(int sig) { /** * block all singal */ -void swSignal_none(void) { +void swoole_signal_block_all(void) { sigset_t mask; sigfillset(&mask); int ret = pthread_sigmask(SIG_BLOCK, &mask, nullptr); if (ret < 0) { - swSysWarn("pthread_sigmask() failed"); + swoole_sys_warning("pthread_sigmask() failed"); } } /** * set new signal handler and return origin signal handler */ -swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int mask) { +SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int mask) { // ignore if (func == nullptr) { func = SIG_IGN; @@ -83,6 +102,11 @@ swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int m 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; @@ -98,13 +122,17 @@ swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int m 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 */ -swSignalHandler swSignal_set(int signo, swSignalHandler handler) { +SignalHandler swoole_signal_set(int signo, SignalHandler handler) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - return swSignalfd_set(signo, handler); + if (SwooleG.enable_signalfd && swoole_event_is_available()) { + return swoole_signalfd_set(signo, handler); } else #endif { @@ -113,71 +141,71 @@ swSignalHandler swSignal_set(int signo, swSignalHandler handler) { // see https://www.freebsd.org/cgi/man.cgi?kqueue // if there's no main reactor, signals cannot be monitored either if (signo != SIGCHLD && sw_reactor()) { - return swKqueueSignal_set(signo, handler); + return swoole_signal_kqueue_set(signo, handler); } else #endif { signals[signo].handler = handler; signals[signo].activated = true; signals[signo].signo = signo; - return swSignal_set(signo, swSignal_async_handler, 1, 0); + return swoole_signal_set(signo, swoole_signal_async_handler, 1, 0); } } } -static void swSignal_async_handler(int signo) { +static void swoole_signal_async_handler(int signo) { if (sw_reactor()) { sw_reactor()->singal_no = signo; } else { // discard signal - if (_lock) { + if (_lock || !SwooleG.init) { return; } _lock = 1; - swSignal_callback(signo); + swoole_signal_callback(signo); _lock = 0; } } -void swSignal_callback(int signo) { +void swoole_signal_callback(int signo) { if (signo >= SW_SIGNO_MAX) { - swWarn("signal[%d] numberis invalid", signo); + swoole_warning("signal[%d] numberis invalid", signo); return; } - swSignalHandler callback = signals[signo].handler; + SignalHandler callback = signals[signo].handler; if (!callback) { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swSignal_str(signo)); + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swoole_signal_to_str(signo)); return; } callback(signo); } -swSignalHandler swSignal_get_handler(int signo) { +SignalHandler swoole_signal_get_handler(int signo) { if (signo >= SW_SIGNO_MAX) { - swWarn("signal[%d] numberis invalid", signo); + swoole_warning("signal[%d] numberis invalid", signo); return nullptr; } else { return signals[signo].handler; } } -void swSignal_clear(void) { +void swoole_signal_clear(void) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swSignalfd_clear(); + 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()) { - swKqueueSignal_set(signals[i].signo, nullptr); + swoole_signal_kqueue_set(signals[i].signo, nullptr); } else #endif { - swSignal_set(signals[i].signo, (swSignalHandler) -1, 1, 0); + swoole_signal_set(signals[i].signo, (SignalHandler) -1, 1, 0); } } } @@ -186,7 +214,7 @@ void swSignal_clear(void) { } #ifdef HAVE_SIGNALFD -void swSignalfd_init() { +void swoole_signalfd_init() { sigemptyset(&signalfd_mask); sw_memset_zero(&signals, sizeof(signals)); } @@ -194,12 +222,12 @@ void swSignalfd_init() { /** * set new signal handler and return origin signal handler */ -static swSignalHandler swSignalfd_set(int signo, swSignalHandler handler) { - swSignalHandler origin_handler = nullptr; +static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler) { + SignalHandler origin_handler = nullptr; if (handler == nullptr && signals[signo].activated) { sigdelset(&signalfd_mask, signo); - sw_memset_zero(&signals[signo], sizeof(swSignal)); + sw_memset_zero(&signals[signo], sizeof(Signal)); } else { sigaddset(&signalfd_mask, signo); origin_handler = signals[signo].handler; @@ -208,36 +236,40 @@ static swSignalHandler swSignalfd_set(int signo, swSignalHandler handler) { signals[signo].activated = true; } - if (sw_reactor()) { - if (signal_fd == 0) { - swSignalfd_create(); - } else { - sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); - signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); - } - swSignalfd_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 swSignalfd_create() { - if (signal_fd != 0) { +static bool swoole_signalfd_create() { + if (swoole_signalfd_is_available()) { return false; } signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); if (signal_fd < 0) { - swSysWarn("signalfd() failed"); + swoole_sys_warning("signalfd() failed"); return false; } signal_socket = swoole::make_socket(signal_fd, SW_FD_SIGNAL); if (sigprocmask(SIG_BLOCK, &signalfd_mask, nullptr) == -1) { - swSysWarn("sigprocmask() failed"); + swoole_sys_warning("sigprocmask() failed"); signal_socket->fd = -1; signal_socket->free(); close(signal_fd); - signal_fd = 0; + signal_socket = nullptr; + signal_fd = -1; return false; } signalfd_create_pid = getpid(); @@ -246,13 +278,13 @@ static bool swSignalfd_create() { return true; } -bool swSignalfd_setup(Reactor *reactor) { - if (signal_fd == 0 && !swSignalfd_create()) { +bool swoole_signalfd_setup(Reactor *reactor) { + if (!swoole_signalfd_is_available() && !swoole_signalfd_create()) { return false; } if (!swoole_event_isset_handler(SW_FD_SIGNAL)) { - swoole_event_set_handler(SW_FD_SIGNAL, swSignalfd_onSignal); - reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, int &event_num) -> bool { + swoole_event_set_handler(SW_FD_SIGNAL, swoole_signalfd_event_callback); + reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, size_t &event_num) -> bool { event_num--; return true; }); @@ -269,33 +301,35 @@ bool swSignalfd_setup(Reactor *reactor) { return true; } -static void swSignalfd_clear() { - if (signal_fd) { +static void swoole_signalfd_clear() { + if (swoole_signalfd_is_available()) { if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, nullptr) < 0) { - swSysWarn("sigprocmask(SIG_UNBLOCK) failed"); + swoole_sys_warning("sigprocmask(SIG_UNBLOCK) failed"); } if (signal_socket) { 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 swSignalfd_onSignal(Reactor *reactor, swEvent *event) { +static int swoole_signalfd_event_callback(Reactor *reactor, Event *event) { struct signalfd_siginfo siginfo; ssize_t n = read(event->fd, &siginfo, sizeof(siginfo)); if (n < 0) { - swSysWarn("read from signalfd failed"); + swoole_sys_warning("read from signalfd failed"); return SW_OK; } if (siginfo.ssi_signo >= SW_SIGNO_MAX) { - swWarn("unknown signal[%d]", siginfo.ssi_signo); + swoole_warning("unknown signal[%d]", siginfo.ssi_signo); return SW_OK; } if (signals[siginfo.ssi_signo].activated) { - swSignalHandler handler = signals[siginfo.ssi_signo].handler; + SignalHandler handler = signals[siginfo.ssi_signo].handler; if (handler == SIG_IGN) { return SW_OK; } else if (handler) { @@ -304,7 +338,7 @@ static int swSignalfd_onSignal(Reactor *reactor, swEvent *event) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, - swSignal_str(siginfo.ssi_signo)); + swoole_signal_to_str(siginfo.ssi_signo)); } } @@ -316,15 +350,15 @@ static int swSignalfd_onSignal(Reactor *reactor, swEvent *event) { /** * set new signal handler and return origin signal handler */ -static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler handler) { +static SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler) { struct kevent ev; - swSignalHandler origin_handler = nullptr; + SignalHandler origin_handler = nullptr; Reactor *reactor = sw_reactor(); // clear signal if (handler == nullptr) { signal(signo, SIG_DFL); - sw_memset_zero(&signals[signo], sizeof(swSignal)); + sw_memset_zero(&signals[signo], sizeof(Signal)); EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); } // add/update signal @@ -334,7 +368,7 @@ static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler 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]); @@ -344,7 +378,7 @@ static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler handler) { } int n = kevent(reactor->native_handle, &ev, 1, nullptr, 0, nullptr); if (n < 0 && sw_unlikely(handler)) { - swSysWarn("kevent set signal[%d] error", signo); + swoole_sys_warning("kevent set signal[%d] error", signo); } return origin_handler; diff --git a/src/os/timer.cc b/src/os/timer.cc index bab72fc5aed..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,10 +23,10 @@ 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); }; - swSignal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; }); + swoole_signal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; }); return true; } @@ -37,7 +37,7 @@ static int SystemTimer_set(Timer *timer, long next_msec) { struct itimerval timer_set; struct timeval now; if (gettimeofday(&now, nullptr) < 0) { - swSysWarn("gettimeofday() failed"); + swoole_sys_warning("gettimeofday() failed"); return SW_ERR; } @@ -58,7 +58,7 @@ static int SystemTimer_set(Timer *timer, long next_msec) { } if (setitimer(ITIMER_REAL, &timer_set, nullptr) < 0) { - swSysWarn("setitimer() failed"); + swoole_sys_warning("setitimer() failed"); return SW_ERR; } return SW_OK; diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc index 70383058443..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,10 +18,9 @@ #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) { - swSysWarn("socketpair() failed"); + swoole_sys_warning("socketpair() failed"); return; } if (!init_socket(socks[1], socks[0])) { @@ -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 f3b86b5cf8e..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; @@ -69,20 +73,15 @@ static void signal_handler(int signo) { static void signal_init() { if (!signal_ready) { Reactor *reactor = SwooleTG.reactor; - swSignal_set(SIGCHLD, signal_handler); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && !reactor->isset_handler(SW_FD_SIGNAL)) { - swSignalfd_setup(reactor); - } -#endif + swoole_signal_set(SIGCHLD, signal_handler); - reactor->set_exit_condition(Reactor::EXIT_CONDITION_WAIT_PID, [](Reactor *reactor, int &event_num) -> bool { + reactor->set_exit_condition(Reactor::EXIT_CONDITION_WAIT_PID, [](Reactor *reactor, size_t &event_num) -> bool { return swoole_coroutine_wait_count() == 0; }); reactor->add_destroy_callback([](void *) { signal_ready = false; - swSignal_clear(); + swoole_signal_clear(); }); signal_ready = true; @@ -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 1da9433b4b4..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 | +----------------------------------------------------------------------+ */ @@ -20,11 +20,12 @@ #include "swoole_string.h" #include "swoole_socket.h" #include "swoole_protocol.h" + 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; @@ -37,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) { - swWarn("invalid package (size=%d) from socket#%u<%s:%d>", - size, - socket->fd, - socket->info.get_ip(), - socket->info.get_port()); + swoole_warning("invalid package (size=%d) from socket#%u<%s:%d>", + pl->buf_size, + socket->fd, + socket->info.get_ip(), + socket->info.get_port()); return SW_ERR; } - swDebug("length=%d", protocol->package_body_offset + body_length); + 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) { @@ -76,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) { @@ -107,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 @@ -134,9 +135,9 @@ 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: - swSysWarn("recv(%d, %d) failed", socket->fd, recv_size); + swoole_sys_warning("recv(%d, %d) failed", socket->fd, recv_size); return SW_OK; case SW_CLOSE: return SW_ERR; @@ -151,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) { @@ -174,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 { @@ -226,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; @@ -236,11 +250,11 @@ 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: - swSysWarn("recv from socket#%d failed", socket->fd); + swoole_sys_warning("recv from socket#%d failed", socket->fd); return SW_OK; case SW_CLOSE: return SW_ERR; @@ -267,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) { @@ -284,7 +300,7 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { // over max length, will discard if (buffer->length == package_max_length) { - swWarn("Package is too big. package_length=%d", (int) buffer->length); + swoole_warning("Package is too big. package_length=%d", (int) buffer->length); return SW_ERR; } @@ -292,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 1008c1c4b1e..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 @@ -6,7 +22,7 @@ namespace dtls { //------------------------------------------------------------------------------- int BIO_write(BIO *b, const char *data, int dlen) { - swTraceLog(SW_TRACE_SSL, "BIO_write(%d)", dlen); + swoole_trace_log(SW_TRACE_SSL, "BIO_write(%d)", dlen); Session *session = (Session *) BIO_get_data(b); return session->socket->write(data, dlen); @@ -20,7 +36,7 @@ int BIO_read(BIO *b, char *data, int len) { if (!session->rxqueue.empty()) { buffer = session->rxqueue.front(); - swTrace("BIO_read(%d, peek=%d)=%d", len, session->peek_mode, buffer->length); + swoole_trace("BIO_read(%d, peek=%d)=%d", len, session->peek_mode, buffer->length); int n = (buffer->length <= len) ? buffer->length : len; memmove(data, buffer->data, n); @@ -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); - swTraceLog(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,8 +103,14 @@ 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: - swWarn("unknown cmd: %d", cmd); + swoole_warning("unknown cmd: %d", cmd); retval = 0; break; } @@ -101,7 +123,7 @@ int BIO_create(BIO *b) { } int BIO_destroy(BIO *b) { - swTraceLog(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; } @@ -170,17 +205,17 @@ bool Session::listen() { return true; } else if (retval < 0) { int reason = ERR_GET_REASON(ERR_peek_error()); - swWarn("DTLSv1_listen() failed, client[%s:%d], reason=%d, error_string=%s", - socket->info.get_ip(), - socket->info.get_port(), - reason, - swSSL_get_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()); return false; } else { listened = true; } - return true; +#endif } //------------------------------------------------------------------------------- diff --git a/src/protocol/http.cc b/src/protocol/http.cc index f3cf89114eb..aa5d8c68335 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -10,21 +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" -using namespace swoole; +#include "thirdparty/multipart_parser.h" + using std::string; using swoole::http_server::Request; using swoole::http_server::StaticHandler; @@ -32,29 +36,108 @@ using swoole::network::SendfileTask; using swoole::network::Socket; // clang-format off -static const char *method_strings[] = -{ +static const char *method_strings[] = { "DELETE", "GET", "HEAD", "POST", "PUT", "PATCH", "CONNECT", "OPTIONS", "TRACE", "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "REPORT", "MKACTIVITY", "CHECKOUT", "MERGE", "M-SEARCH", "NOTIFY", "SUBSCRIBE", "UNSUBSCRIBE", "PURGE", "PRI", }; // clang-format on -int swHttp_get_method(const char *method_str, size_t method_len) { - int i = 0; - for (; i < SW_HTTP_PRI; i++) { - if (swoole_strcaseeq(method_strings[i], strlen(method_strings[i]), method_str, method_len)) { - return i + 1; - } +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); } - return -1; } -const char *swHttp_get_method_string(int method) { - if (method < 0 || method > SW_HTTP_PRI) { - return nullptr; +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; } - return method_strings[method - 1]; + + 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) { @@ -69,7 +152,7 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co char header_buffer[1024]; SendData response; response.info.fd = conn->session_id; - response.info.type = SW_SERVER_EVENT_RECV_DATA; + response.info.type = SW_SERVER_EVENT_SEND_DATA; if (handler.status_code == SW_HTTP_NOT_FOUND) { response.info.len = sw_snprintf(header_buffer, @@ -78,7 +161,7 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n" "Content-Length: %zu\r\n" "\r\n%s", - swHttp_get_status_message(SW_HTTP_NOT_FOUND), + http_server::get_status_message(SW_HTTP_NOT_FOUND), sizeof(SW_HTTP_PAGE_404) - 1, SW_HTTP_PAGE_404); response.data = header_buffer; @@ -90,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); @@ -109,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(), @@ -160,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; @@ -185,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 @@ -203,7 +320,205 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } -const char *swHttp_get_status_message(int code) { +void Server::destroy_http_request(Connection *conn) { + auto request = reinterpret_cast(conn->object); + if (!request) { + return; + } + delete request; + 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: return "100 Continue"; @@ -295,6 +610,8 @@ const char *swHttp_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: @@ -323,7 +640,90 @@ const char *swHttp_get_status_message(int code) { } } -static int sw_htoi(char *s) { +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; @@ -343,7 +743,7 @@ static int sw_htoi(char *s) { } /* return value: length of decoded string */ -size_t swHttp_url_decode(char *str, size_t len) { +size_t url_decode(char *str, size_t len) { char *dest = str; char *data = str; @@ -351,7 +751,7 @@ size_t swHttp_url_decode(char *str, size_t len) { if (*data == '+') { *dest = ' '; } else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { - *dest = (char) sw_htoi(data + 1); + *dest = (char) url_htoi(data + 1); data += 2; len -= 2; } else { @@ -365,7 +765,7 @@ size_t swHttp_url_decode(char *str, size_t len) { return dest - str; } -char *swHttp_url_encode(char const *str, size_t len) { +char *url_encode(char const *str, size_t len) { static uchar hexchars[] = "0123456789ABCDEF"; size_t x, y; @@ -395,9 +795,6 @@ char *swHttp_url_encode(char const *str, size_t len) { return ret; } -namespace swoole { -namespace http_server { -//----------------------------------------------------------------- /** * only GET/POST */ @@ -459,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; @@ -469,9 +865,7 @@ int Request::get_protocol() { } else { goto _excepted; } - } -#endif - else { + } else { _excepted: excepted = 1; return SW_ERR; @@ -499,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) { @@ -532,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; + } } } } @@ -573,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; @@ -600,7 +1076,6 @@ bool Request::has_expect_header() { } return false; } -#endif int Request::get_header_length() { char *p = buffer_->str + buffer_->offset; @@ -623,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; } @@ -652,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: @@ -687,22 +1178,35 @@ string Request::get_date_if_modified_since() { } } - return string(""); + return string(); } -//----------------------------------------------------------------- -} // namespace http_server -} // namespace swoole -void Server::destroy_http_request(Connection *conn) { - auto request = reinterpret_cast(conn->object); - if (!request) { - return; +int get_method(const char *method_str, size_t method_len) { + int i = 0; + for (; i < SW_HTTP_PRI; i++) { + if (swoole_strcaseeq(method_strings[i], strlen(method_strings[i]), method_str, method_len)) { + return i + 1; + } } - delete request; - conn->object = nullptr; + return -1; } -#ifdef SW_USE_HTTP2 +const char *get_method_string(int method) { + if (method < 0 || method > SW_HTTP_PRI) { + return nullptr; + } + return method_strings[method - 1]; +} + +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,22 +1216,22 @@ static void protocol_status_error(Socket *socket, Connection *conn) { conn->info.get_port()); } -ssize_t swHttpMix_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; } } -uint8_t swHttpMix_get_package_length_size(Socket *socket) { +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,15 +1240,16 @@ uint8_t swHttpMix_get_package_length_size(Socket *socket) { } } -int swHttpMix_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/protocol/message_bus.cc b/src/protocol/message_bus.cc new file mode 100644 index 00000000000..0f6676daf09 --- /dev/null +++ b/src/protocol/message_bus.cc @@ -0,0 +1,312 @@ +#include "swoole_message_bus.h" + +#include + +using swoole::network::Address; +using swoole::network::Socket; + +namespace swoole { + +PacketPtr MessageBus::get_packet() const { + PacketPtr pkt; + if (buffer_->info.flags & SW_EVENT_DATA_PTR) { + memcpy(&pkt, buffer_->data, sizeof(pkt)); + } else if (buffer_->info.flags & SW_EVENT_DATA_OBJ_PTR) { + String *object; + memcpy(&object, buffer_->data, sizeof(object)); + pkt.data = object->str; + pkt.length = object->length; + } else { + pkt.data = buffer_->data; + pkt.length = buffer_->info.len; + } + + return pkt; +} + +String *MessageBus::get_packet_buffer() { + String *packet_buffer = nullptr; + + auto iter = packet_pool_.find(buffer_->info.msg_id); + if (iter == packet_pool_.end()) { + if (!buffer_->is_begin()) { + return nullptr; + } + packet_buffer = make_string(buffer_->info.len, allocator_); + packet_pool_.emplace(buffer_->info.msg_id, std::shared_ptr(packet_buffer)); + } else { + packet_buffer = iter->second.get(); + } + + return packet_buffer; +} + +ReturnCode MessageBus::prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer) { + recv_chunk_count++; + if (!buffer_->is_end()) { + /** + * if the reactor thread sends too many chunks to the worker process, + * the worker process may receive chunks all the time, + * resulting in the worker process being unable to handle other tasks. + * in order to make the worker process handle tasks fairly, + * the maximum number of consecutive chunks received by the worker is limited. + */ + if (recv_chunk_count >= SW_WORKER_MAX_RECV_CHUNK_COUNT) { + swoole_trace_log(SW_TRACE_WORKER, + "worker process[%u] receives the chunk data to the maximum[%d], return to event loop", + SwooleG.process_id, + recv_chunk_count); + return SW_WAIT; + } + return SW_CONTINUE; + } else { + /** + * Because we don't want to split the EventData parameters into DataHead and data, + * we store the value of the worker_buffer pointer in EventData.data. + * The value of this pointer will be fetched in the Server::get_pipe_packet() function. + */ + buffer_->info.flags |= SW_EVENT_DATA_OBJ_PTR; + memcpy(buffer_->data, &packet_buffer, sizeof(packet_buffer)); + 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; + DataHead *info = &buffer_->info; + struct iovec buffers[2]; + +_read_from_pipe: + recv_n = recv(sock->get_fd(), info, sizeof(buffer_->info), MSG_PEEK); + if (recv_n < 0) { + if (sock->catch_read_error(errno) == SW_WAIT) { + return SW_OK; + } + return SW_ERR; + } else if (recv_n == 0) { + swoole_warning("receive data from socket#%d returns 0", sock->get_fd()); + return SW_ERR; + } + + if (!buffer_->is_chunked()) { + return sock->read(buffer_, sizeof(buffer_->info) + buffer_->info.len); + } + + auto packet_buffer = get_packet_buffer(); + if (packet_buffer == nullptr) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", + info->msg_id, + sock->get_fd(), + info->reactor_id); + return SW_OK; + } + + size_t remain_len = buffer_->info.len - packet_buffer->length; + buffers[0].iov_base = info; + buffers[0].iov_len = sizeof(buffer_->info); + buffers[1].iov_base = packet_buffer->str + packet_buffer->length; + buffers[1].iov_len = SW_MIN(buffer_size_ - sizeof(buffer_->info), remain_len); + + recv_n = readv(sock->get_fd(), buffers, 2); + if (recv_n == 0) { + 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_read_error(errno) == SW_WAIT) { + return SW_OK; + } + if (recv_n > 0) { + packet_buffer->length += (recv_n - sizeof(buffer_->info)); + 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)) { + case SW_READY: + return recv_n; + case SW_CONTINUE: + goto _read_from_pipe; + case SW_WAIT: + return SW_OK; + default: + assert(0); + return SW_ERR; + } +} + +/** + * Notice: only supports dgram type socket + */ +ssize_t MessageBus::read_with_buffer(network::Socket *sock) { + ssize_t recv_n; + uint16_t recv_chunk_count = 0; + +_read_from_pipe: + recv_n = sock->read(buffer_, buffer_size_); + if (recv_n < 0) { + if (sock->catch_read_error(errno) == SW_WAIT) { + return SW_OK; + } + return SW_ERR; + } else if (recv_n == 0) { + swoole_warning("receive data from socket#%d returns 0", sock->get_fd()); + return SW_ERR; + } + + recv_chunk_count++; + + if (!buffer_->is_chunked()) { + return recv_n; + } + + String *packet_buffer = get_packet_buffer(); + if (packet_buffer == nullptr) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", + buffer_->info.msg_id, + sock->get_fd(), + buffer_->info.reactor_id); + return SW_ERR; + } + packet_buffer->append(buffer_->data, recv_n - sizeof(buffer_->info)); + + switch (prepare_packet(recv_chunk_count, packet_buffer)) { + case SW_READY: + return recv_n; + case SW_CONTINUE: + goto _read_from_pipe; + case SW_WAIT: + return SW_OK; + default: + assert(0); + return SW_ERR; + } +} + +bool MessageBus::write(Socket *sock, SendData *resp) { + const char *payload = resp->data; + uint32_t l_payload = resp->info.len; + off_t offset = 0; + uint32_t copy_n; + + struct iovec iov[2]; + + uint64_t msg_id = id_generator_(); + uint32_t max_length = buffer_size_ - sizeof(resp->info); + resp->info.msg_id = msg_id; + + auto send_fn = [](Socket *sock, const iovec *iov, size_t iovcnt) { + if (swoole_event_is_available()) { + return swoole_event_writev(sock, iov, iovcnt); + } else { + return sock->writev_blocking(iov, iovcnt); + } + }; + + if (l_payload == 0 || payload == nullptr) { + resp->info.flags = 0; + resp->info.len = 0; + iov[0].iov_base = &resp->info; + iov[0].iov_len = sizeof(resp->info); + return send_fn(sock, iov, 1) == (ssize_t) iov[0].iov_len; + } + + if (!always_chunked_transfer_ && l_payload <= max_length) { + resp->info.flags = 0; + resp->info.len = l_payload; + iov[0].iov_base = &resp->info; + iov[0].iov_len = sizeof(resp->info); + iov[1].iov_base = (void *) payload; + iov[1].iov_len = 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; + resp->info.len = l_payload; + + while (l_payload > 0) { + if (l_payload > max_length) { + copy_n = max_length; + } else { + resp->info.flags |= SW_EVENT_DATA_END; + copy_n = l_payload; + } + + iov[0].iov_base = &resp->info; + iov[0].iov_len = sizeof(resp->info); + iov[1].iov_base = (void *) (payload + offset); + iov[1].iov_len = copy_n; + + swoole_trace("finish, type=%d|len=%u", resp->info.type, copy_n); + + if (send_fn(sock, iov, 2) < 0) { + 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; + } + return false; + } + + if (resp->info.flags & SW_EVENT_DATA_BEGIN) { + resp->info.flags &= ~SW_EVENT_DATA_BEGIN; + } + + l_payload -= copy_n; + offset += copy_n; + } + + return true; +} + +size_t MessageBus::get_memory_size() { + size_t size = buffer_size_; + 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 52ee1aaf07d..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 | +----------------------------------------------------------------------+ */ @@ -20,29 +20,31 @@ #include "swoole_mqtt.h" #include "swoole_protocol.h" -using swoole::Protocol; using swoole::network::Socket; -void swMqtt_print_package(swMqtt_packet *pkg) { +namespace swoole { +namespace mqtt { + +void print_package(Packet *pkg) { printf("type=%d, length=%d\n", pkg->type, pkg->length); } -void swMqtt_set_protocol(Protocol *protocol) { +void set_protocol(Protocol *protocol) { protocol->package_length_size = SW_MQTT_MAX_LENGTH_SIZE; protocol->package_length_offset = 1; protocol->package_body_offset = 0; - protocol->get_package_length = swMqtt_get_package_length; + protocol->get_package_length = get_package_length; } // recv variable_header packet twice may cause that the '*data' contain the payload data, // but there's no chance to read the next mqtt request ,because MQTT client will recv ACK blocking -#define MQTT_RECV_LEN_AGAIN 0 +#define SW_MQTT_RECV_LEN_AGAIN 0 -ssize_t swMqtt_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 MQTT_RECV_LEN_AGAIN; + return SW_MQTT_RECV_LEN_AGAIN; } uint8_t byte; @@ -51,7 +53,7 @@ ssize_t swMqtt_get_package_length(Protocol *protocol, Socket *conn, const char * 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 @@ -65,9 +67,12 @@ ssize_t swMqtt_get_package_length(Protocol *protocol, Socket *conn, const char * return SW_ERR; } if (variable_header_byte_count >= recv_variable_header_size) { // length not enough - return MQTT_RECV_LEN_AGAIN; + return SW_MQTT_RECV_LEN_AGAIN; } } // payload_length + variable_header_byte_count + length_offset(1) return length + variable_header_byte_count + 1; } + +} // namespace mqtt +} // namespace swoole diff --git a/src/protocol/redis.cc b/src/protocol/redis.cc index d54239af2ab..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 | +----------------------------------------------------------------------+ */ @@ -23,9 +23,10 @@ #include "swoole_server.h" #include "swoole_redis.h" -using namespace swoole; +namespace swoole { +namespace redis { -struct RedisRequest { +struct Request { uint8_t state; int n_lines_total; @@ -37,25 +38,25 @@ struct RedisRequest { int offset; }; -int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { +int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { const char *p, *pe; int ret; char *buf_ptr; size_t buf_size; - - RedisRequest *request; + RecvData rdata{}; + Request *request; network::Socket *socket = conn->socket; if (conn->object == nullptr) { - request = (RedisRequest *) sw_malloc(sizeof(RedisRequest)); + request = (Request *) sw_malloc(sizeof(Request)); if (!request) { - swWarn("malloc(%ld) failed", sizeof(RedisRequest)); + swoole_warning("malloc(%ld) failed", sizeof(Request)); return SW_ERR; } - sw_memset_zero(request, sizeof(RedisRequest)); + sw_memset_zero(request, sizeof(Request)); conn->object = request; } else { - request = (RedisRequest *) conn->object; + request = (Request *) conn->object; } _recv_data: @@ -64,9 +65,9 @@ int swRedis_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: - swSysWarn("recv from socket#%d failed", conn->fd); + swoole_sys_warning("recv from socket#%d failed", conn->fd); return SW_OK; case SW_CLOSE: return SW_ERR; @@ -80,7 +81,7 @@ int swRedis_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; } @@ -89,7 +90,7 @@ int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { } } else if (buffer->length == buffer->size) { _package_too_big: - swWarn("Package is too big. package_length=%ld", buffer->length); + swoole_warning("Package is too big. package_length=%ld", buffer->length); return SW_ERR; } goto _recv_data; @@ -100,19 +101,22 @@ int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { do { switch (request->state) { - case SW_REDIS_RECEIVE_TOTAL_LINE: - if (*p == '*' && (p = swRedis_get_number(p, &ret))) { + case STATE_RECEIVE_TOTAL_LINE: + if (*p == '*') { + if ((p = get_number(p, &ret)) == nullptr) { + goto _failed; + } request->n_lines_total = ret; - request->state = SW_REDIS_RECEIVE_LENGTH; + request->state = STATE_RECEIVE_LENGTH; break; } - if (p == nullptr) { - goto _failed; - } /* no break */ - case SW_REDIS_RECEIVE_LENGTH: - if (*p == '$' && (p = swRedis_get_number(p, &ret))) { + case STATE_RECEIVE_LENGTH: + if (*p == '$') { + if ((p = get_number(p, &ret)) == nullptr) { + goto _failed; + } if (ret < 0) { break; } @@ -120,19 +124,19 @@ int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { goto _package_too_big; } request->n_bytes_total = ret; - request->state = SW_REDIS_RECEIVE_STRING; + request->state = STATE_RECEIVE_STRING; break; } // integer - else if (*p == ':' && (p = swRedis_get_number(p, &ret))) { + else if (*p == ':') { + if ((p = get_number(p, &ret)) == nullptr) { + goto _failed; + } break; } - if (p == nullptr) { - goto _failed; - } /* no break */ - case SW_REDIS_RECEIVE_STRING: + case STATE_RECEIVE_STRING: if (pe - p < request->n_bytes_total - request->n_bytes_received) { request->n_bytes_received += pe - p; return SW_OK; @@ -140,18 +144,20 @@ int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { p += request->n_bytes_total + SW_CRLF_LEN; request->n_bytes_total = 0; request->n_lines_received++; - request->state = SW_REDIS_RECEIVE_LENGTH; + request->state = STATE_RECEIVE_LENGTH; 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) { return SW_OK; } buffer->clear(); - sw_memset_zero(request, sizeof(RedisRequest)); + sw_memset_zero(request, sizeof(Request)); return SW_OK; } } @@ -163,28 +169,28 @@ int swRedis_recv_packet(Protocol *protocol, Connection *conn, String *buffer) { } while (p < pe); } _failed: - swWarn("redis protocol error"); + swoole_warning("redis protocol error"); return SW_ERR; } -bool swRedis_format(String *buf) { +bool format(String *buf) { return buf->append(SW_STRL(SW_REDIS_RETURN_NIL)) == SW_OK; } -bool swRedis_format(String *buf, enum swRedis_reply_type type, const std::string &value) { - if (type == SW_REDIS_REPLY_STATUS) { +bool format(String *buf, enum ReplyType type, const std::string &value) { + if (type == REPLY_STATUS) { if (value.empty()) { return buf->append(SW_STRL("+OK\r\n")) == SW_OK; } else { return buf->format("+%.*s\r\n", value.length(), value.c_str()) > 0; } - } else if (type == SW_REDIS_REPLY_ERROR) { + } else if (type == REPLY_ERROR) { if (value.empty()) { return buf->append(SW_STRL("-ERR\r\n")) == SW_OK; } else { return buf->format("-%.*s\r\n", value.length(), value.c_str()) > 0; } - } else if (type == SW_REDIS_REPLY_STRING) { + } else if (type == REPLY_STRING) { if (value.empty() or value.length() > SW_REDIS_MAX_STRING_SIZE) { return false; } else { @@ -199,12 +205,12 @@ bool swRedis_format(String *buf, enum swRedis_reply_type type, const std::string return false; } -bool swRedis_format(String *buf, enum swRedis_reply_type type, long value) { +bool format(String *buf, enum ReplyType type, long value) { return buf->format(":%" PRId64 "\r\n", value) > 0; } -std::vector swRedis_parse(const char *data, size_t len) { - int state = SW_REDIS_RECEIVE_TOTAL_LINE; +std::vector parse(const char *data, size_t len) { + int state = STATE_RECEIVE_TOTAL_LINE; const char *p = data; const char *pe = p + len; @@ -214,33 +220,33 @@ std::vector swRedis_parse(const char *data, size_t len) { std::vector result; do { switch (state) { - case SW_REDIS_RECEIVE_TOTAL_LINE: - if (*p == '*' && (p = swRedis_get_number(p, &ret))) { - state = SW_REDIS_RECEIVE_LENGTH; + case STATE_RECEIVE_TOTAL_LINE: + if (*p == '*' && (p = get_number(p, &ret))) { + state = STATE_RECEIVE_LENGTH; break; } /* no break */ - case SW_REDIS_RECEIVE_LENGTH: - if (*p == '$' && (p = swRedis_get_number(p, &ret))) { + case STATE_RECEIVE_LENGTH: + if (*p == '$' && (p = get_number(p, &ret))) { if (ret == -1) { break; } length = ret; - state = SW_REDIS_RECEIVE_STRING; + state = STATE_RECEIVE_STRING; break; } // integer - else if (*p == ':' && (p = swRedis_get_number(p, &ret))) { + else if (*p == ':' && (p = get_number(p, &ret))) { result.push_back(std::to_string(ret)); break; } /* no break */ - case SW_REDIS_RECEIVE_STRING: + case STATE_RECEIVE_STRING: result.push_back(std::string(p, length)); p += length + SW_CRLF_LEN; - state = SW_REDIS_RECEIVE_LENGTH; + state = STATE_RECEIVE_LENGTH; break; default: @@ -250,3 +256,5 @@ std::vector swRedis_parse(const char *data, size_t len) { return result; } +} // namespace redis +} // namespace swoole 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 d4a0e980716..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 | +----------------------------------------------------------------------+ */ @@ -18,12 +18,13 @@ #include "swoole_string.h" #include "swoole_socket.h" #include "swoole_ssl.h" +#include "swoole_util.h" #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" @@ -35,17 +36,17 @@ static int ssl_connection_index = 0; static int ssl_port_index = 0; static pthread_mutex_t *lock_array; -static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store); +static int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); #ifndef OPENSSL_NO_RSA -static RSA *swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length); +static RSA *swoole_ssl_rsa_key_callback(SSL *ssl, int is_export, int key_length); #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L -static int swSSL_set_default_dhparam(SSL_CTX *ssl_context); +static int swoole_ssl_set_default_dhparam(SSL_CTX *ssl_context); #endif #ifdef SW_SUPPORT_DTLS -static int swSSL_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len); -static int swSSL_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len); +static int swoole_ssl_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len); +static int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len); #endif #ifdef __GNUC__ @@ -54,9 +55,13 @@ static int swSSL_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len); #define MAYBE_UNUSED #endif -static void MAYBE_UNUSED swSSL_lock_callback(int mode, int type, const char *file, int line); +std::string swoole_ssl_get_version_message() { + return swoole::std_string::format("OPENSSL_VERSION: %s\n", OPENSSL_VERSION_TEXT); +} -void swSSL_init(void) { +static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line); + +void swoole_ssl_init(void) { if (openssl_init) { return; } @@ -72,28 +77,28 @@ void swSSL_init(void) { ssl_connection_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); if (ssl_connection_index < 0) { - swError("SSL_get_ex_new_index() failed"); + swoole_error("SSL_get_ex_new_index() failed"); return; } ssl_port_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); if (ssl_port_index < 0) { - swError("SSL_get_ex_new_index() failed"); + swoole_error("SSL_get_ex_new_index() failed"); return; } openssl_init = true; } -int swSSL_get_ex_connection_index() { +int swoole_ssl_get_ex_connection_index() { return ssl_connection_index; } -int swSSL_get_ex_port_index() { +int swoole_ssl_get_ex_port_index() { return ssl_port_index; } -void swSSL_destroy() { +void swoole_ssl_destroy() { if (!openssl_init) { return; } @@ -114,7 +119,7 @@ void swSSL_destroy() { openssl_thread_safety_init = false; } -static void MAYBE_UNUSED swSSL_lock_callback(int mode, int type, const char *file, int line) { +static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line) { if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&(lock_array[type])); } else { @@ -128,23 +133,23 @@ static int ssl_error_cb(const char *str, size_t len, void *buf) { return 0; } -const char *swSSL_get_error() { +const char *swoole_ssl_get_error() { ERR_print_errors_cb(ssl_error_cb, sw_tg_buffer()->str); return sw_tg_buffer()->str; } #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 -static void MAYBE_UNUSED swSSL_id_callback(CRYPTO_THREADID *id) { +static void MAYBE_UNUSED swoole_ssl_id_callback(CRYPTO_THREADID *id) { CRYPTO_THREADID_set_numeric(id, (ulong_t) pthread_self()); } #else -static ulong_t swSSL_id_callback(void) { +static ulong_t swoole_ssl_id_callback(void) { return (ulong_t) pthread_self(); } #endif -void swSSL_init_thread_safety() { +void swoole_ssl_init_thread_safety() { if (!openssl_init) { return; } @@ -159,20 +164,20 @@ void swSSL_init_thread_safety() { } #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 - (void) CRYPTO_THREADID_set_callback(swSSL_id_callback); + (void) CRYPTO_THREADID_set_callback(swoole_ssl_id_callback); #else - CRYPTO_set_id_callback(swSSL_id_callback); + CRYPTO_set_id_callback(swoole_ssl_id_callback); #endif - CRYPTO_set_locking_callback(swSSL_lock_callback); + CRYPTO_set_locking_callback(swoole_ssl_lock_callback); openssl_thread_safety_init = true; } -bool swSSL_is_thread_safety() { +bool swoole_ssl_is_thread_safety() { return openssl_thread_safety_init; } -static void swSSL_info_callback(const SSL *ssl, int where, int ret) { +static void swoole_ssl_info_callback(const SSL *ssl, int where, int ret) { BIO *rbio, *wbio; swSocket *sock; @@ -181,7 +186,7 @@ static void swSSL_info_callback(const SSL *ssl, int where, int ret) { if (sock->ssl_state == SW_SSL_STATE_READY) { sock->ssl_renegotiation = 1; - swDebug("SSL renegotiation"); + swoole_debug("SSL renegotiation"); } } @@ -215,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) { @@ -237,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; @@ -267,22 +270,22 @@ static int ssl_select_next_proto_cb(SSL *ssl, uchar **out, uchar *outlen, const for (unsigned int i = 0; i < inlen; i += in[i] + 1) { info += " * " + std::string(reinterpret_cast(&in[i + 1]), in[i]); } - swTraceLog(SW_TRACE_HTTP2, "[NPN] server offers: %s", info.c_str()); + 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)) { - swWarn("HTTP/2 protocol was not selected, expects [h2]"); + 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) { @@ -296,7 +299,7 @@ static int ssl_passwd_callback(char *buf, int num, int verify, void *data) { bool SSLContext::create() { if (!openssl_init) { - swSSL_init(); + swoole_ssl_init(); } const SSL_METHOD *method; @@ -314,7 +317,7 @@ bool SSLContext::create() { context = SSL_CTX_new(method); if (context == nullptr) { int error = ERR_get_error(); - swWarn("SSL_CTX_new() failed, Error: %s[%d]", ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_new() failed, Error: %s[%d]", ERR_reason_error_string(error), error); return false; } @@ -410,7 +413,7 @@ bool SSLContext::create() { #endif SSL_CTX_set_read_ahead(context, 1); - SSL_CTX_set_info_callback(context, swSSL_info_callback); + SSL_CTX_set_info_callback(context, swoole_ssl_info_callback); if (!passphrase.empty()) { SSL_CTX_set_default_passwd_cb_userdata(context, this); @@ -423,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(); - swWarn("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; } /* @@ -433,8 +438,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_certificate_chain_file(context, cert_file.c_str()) <= 0) { int error = ERR_get_error(); - swWarn("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; } } @@ -444,23 +451,27 @@ bool SSLContext::create() { */ if (SSL_CTX_use_PrivateKey_file(context, key_file.c_str(), SSL_FILETYPE_PEM) <= 0) { int error = ERR_get_error(); - swWarn("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; } /* * verify private key */ if (!SSL_CTX_check_private_key(context)) { - swWarn("Private key does not match the public certificate"); + swoole_warning("Private key does not match the public certificate"); return false; } } #ifdef SW_SUPPORT_DTLS if (protocols & SW_SSL_DTLS) { - SSL_CTX_set_cookie_generate_cb(context, swSSL_generate_cookie); - SSL_CTX_set_cookie_verify_cb(context, swSSL_verify_cookie); +#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 @@ -472,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; } @@ -495,13 +509,17 @@ bool SSLContext::create() { } #endif +#ifdef OPENSSL_IS_BORINGSSL + SSL_CTX_set_grease_enabled(context, grease); +#endif + if (!client_cert_file.empty() && !set_client_certificate()) { - swWarn("set_client_certificate() error"); + swoole_warning("set_client_certificate() error"); return false; } if (!set_ciphers()) { - swWarn("set_cipher() error"); + swoole_warning("set_cipher() error"); return false; } @@ -517,7 +535,7 @@ bool SSLContext::set_capath() { } } else { if (!SSL_CTX_set_default_verify_paths(context)) { - swWarn("Unable to set default verify locations and no CA settings specified"); + swoole_warning("Unable to set default verify locations and no CA settings specified"); return false; } } @@ -536,7 +554,7 @@ bool SSLContext::set_ciphers() { if (!ciphers.empty()) { if (SSL_CTX_set_cipher_list(context, ciphers.c_str()) == 0) { - swWarn("SSL_CTX_set_cipher_list(\"%s\") failed", ciphers.c_str()); + swoole_warning("SSL_CTX_set_cipher_list(\"%s\") failed", ciphers.c_str()); return false; } if (prefer_server_ciphers) { @@ -545,7 +563,7 @@ bool SSLContext::set_ciphers() { } #ifndef OPENSSL_NO_RSA - SSL_CTX_set_tmp_rsa_callback(context, swSSL_rsa_key_callback); + SSL_CTX_set_tmp_rsa_callback(context, swoole_ssl_rsa_key_callback); #endif if (!dhparam.empty() && !set_dhparam()) { @@ -553,7 +571,7 @@ bool SSLContext::set_ciphers() { } #if OPENSSL_VERSION_NUMBER < 0x10100000L else { - swSSL_set_default_dhparam(context); + swoole_ssl_set_default_dhparam(context); } #endif if (!ecdh_curve.empty() && !set_ecdh_curve()) { @@ -568,18 +586,18 @@ bool SSLContext::set_client_certificate() { const char *cert_file = client_cert_file.c_str(); int depth = verify_depth; - SSL_CTX_set_verify(context, SSL_VERIFY_PEER, swSSL_verify_callback); + SSL_CTX_set_verify(context, SSL_VERIFY_PEER, swoole_ssl_verify_callback); SSL_CTX_set_verify_depth(context, depth); if (SSL_CTX_load_verify_locations(context, cert_file, nullptr) == 0) { - swWarn("SSL_CTX_load_verify_locations(\"%s\") failed", cert_file); + swoole_warning("SSL_CTX_load_verify_locations(\"%s\") failed", cert_file); return false; } ERR_clear_error(); list = SSL_load_client_CA_file(cert_file); if (list == nullptr) { - swWarn("SSL_load_client_CA_file(\"%s\") failed", cert_file); + swoole_warning("SSL_load_client_CA_file(\"%s\") failed", cert_file); return false; } @@ -618,7 +636,7 @@ bool SSLContext::set_ecdh_curve() { return true; } if (SSL_CTX_set1_curves_list(context, ecdh_curve.c_str()) == 0) { - swWarn("SSL_CTX_set1_curves_list(\"%s\") failed", ecdh_curve.c_str()); + swoole_warning("SSL_CTX_set1_curves_list(\"%s\") failed", ecdh_curve.c_str()); return false; } #else @@ -631,13 +649,13 @@ bool SSLContext::set_ecdh_curve() { */ int nid = OBJ_sn2nid(ecdh_curve.c_str()); if (nid == 0) { - swWarn("Unknown curve name \"%s\"", ecdh_curve.c_str()); + swoole_warning("Unknown curve name \"%s\"", ecdh_curve.c_str()); return false; } ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == nullptr) { - swWarn("Unable to create curve \"%s\"", ecdh_curve.c_str()); + swoole_warning("Unable to create curve \"%s\"", ecdh_curve.c_str()); return false; } @@ -659,13 +677,13 @@ bool SSLContext::set_dhparam() { bio = BIO_new_file(file, "r"); if (bio == nullptr) { - swWarn("BIO_new_file(%s) failed", file); + swoole_warning("BIO_new_file(%s) failed", file); return false; } dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { - swWarn("PEM_read_bio_DHparams(%s) failed", file); + swoole_warning("PEM_read_bio_DHparams(%s) failed", file); BIO_free(bio); return false; } @@ -682,9 +700,9 @@ SSLContext::~SSLContext() { SSL_CTX_free(context); } -} +} // namespace swoole -static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store) { +static int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { #if 0 char *subject, *issuer; int err, depth; @@ -700,7 +718,7 @@ static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store) { iname = X509_get_issuer_name(cert); issuer = iname ? X509_NAME_oneline(iname, nullptr, 0) : "(none)"; - swWarn("verify:%d, error:%d, depth:%d, subject:\"%s\", issuer:\"%s\"", ok, err, depth, subject, issuer); + swoole_warning("verify:%d, error:%d, depth:%d, subject:\"%s\", issuer:\"%s\"", ok, err, depth, subject, issuer); if (sname) { @@ -729,7 +747,7 @@ static void calculate_cookie(SSL *ssl, uchar *cookie_secret, uint cookie_length) } } -static int swSSL_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) { +static int swoole_ssl_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) { uchar *buffer, result[EVP_MAX_MD_SIZE]; uint length = 0, result_len; Address sa{}; @@ -757,7 +775,7 @@ static int swSSL_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) { buffer = (uchar *) OPENSSL_malloc(length); if (buffer == nullptr) { - swSysWarn("out of memory"); + swoole_sys_warning("out of memory"); return 0; } @@ -784,18 +802,18 @@ static int swSSL_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) { return 1; } -static int swSSL_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len) { +static int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len) { uint result_len = 0; uchar result[COOKIE_SECRET_LENGTH]; - swSSL_generate_cookie(ssl, result, &result_len); + swoole_ssl_generate_cookie(ssl, result, &result_len); return cookie_len == result_len && memcmp(result, cookie, result_len) == 0; } #endif #ifndef OPENSSL_NO_RSA -static RSA *swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length) { +static RSA *swoole_ssl_rsa_key_callback(SSL *ssl, int is_export, int key_length) { static RSA *rsa_tmp = nullptr; if (rsa_tmp) { return rsa_tmp; @@ -803,7 +821,7 @@ static RSA *swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length) { BIGNUM *bn = BN_new(); if (bn == nullptr) { - swWarn("allocation error generating RSA key"); + swoole_warning("allocation error generating RSA key"); return nullptr; } @@ -820,7 +838,7 @@ static RSA *swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length) { #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L -static int swSSL_set_default_dhparam(SSL_CTX *ssl_context) { +static int swoole_ssl_set_default_dhparam(SSL_CTX *ssl_context) { DH *dh; static unsigned char dh1024_p[] = { 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, 0x80, 0xE9, 0xCF, 0xDB, @@ -835,7 +853,7 @@ static int swSSL_set_default_dhparam(SSL_CTX *ssl_context) { static unsigned char dh1024_g[] = {0x02}; dh = DH_new(); if (dh == nullptr) { - swWarn("DH_new() failed"); + swoole_warning("DH_new() failed"); return SW_ERR; } diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 1acc6a6b044..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; } } - swTraceLog(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,54 +165,50 @@ 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; } - swTraceLog(SW_TRACE_WEBSOCKET, - "decode frame, payload_length=%ld, mask=%d, opcode=%d", - payload_length, - frame->header.MASK, - frame->header.OPCODE); + frame->payload_length = total_length - pl.header_len; + frame->header_length = pl.header_len; - if (payload_length == 0) { - frame->header_length = header_length; - frame->payload_length = 0; - frame->payload = nullptr; - - 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; } int pack_close_frame(String *buffer, int code, char *reason, size_t length, uint8_t flags) { if (sw_unlikely(length > SW_WEBSOCKET_CLOSE_REASON_MAX_LEN)) { - swWarn("the max length of close reason is %d", SW_WEBSOCKET_CLOSE_REASON_MAX_LEN); + swoole_warning("the max length of close reason is %d", SW_WEBSOCKET_CLOSE_REASON_MAX_LEN); return SW_ERR; } @@ -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,28 +240,32 @@ 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; - swListenPort *port; + ListenPort *port; size_t offset; switch (ws.header.OPCODE) { case OPCODE_CONTINUATION: frame_buffer = conn->websocket_buffer; if (frame_buffer == nullptr) { - swWarn("bad frame[opcode=0]. remote_addr=%s:%d", conn->info.get_ip(), conn->info.get_port()); + swoole_warning("bad frame[opcode=0]. remote_addr=%s:%d", conn->info.get_ip(), conn->info.get_port()); return SW_ERR; } offset = length - ws.payload_length; @@ -268,16 +273,17 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t port = serv->get_port_by_fd(conn->fd); // frame data overflow if (frame_buffer->length + frame_length > port->protocol.package_max_length) { - swWarn("websocket frame is too big, remote_addr=%s:%d", conn->info.get_ip(), conn->info.get_port()); + swoole_warning("websocket frame is too big, remote_addr=%s:%d", conn->info.get_ip(), conn->info.get_port()); return SW_ERR; } // merge incomplete data 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,40 +292,42 @@ 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) { - swWarn("merging incomplete frame, bad request. remote_addr=%s:%d", - conn->info.get_ip(), - conn->info.get_port()); + swoole_warning("merging incomplete frame, bad request. remote_addr=%s:%d", + 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; } case OPCODE_PING: case OPCODE_PONG: if (length >= (sizeof(buf) - SW_WEBSOCKET_HEADER_LEN)) { - swWarn("%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()); + 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()); 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 @@ -349,7 +358,7 @@ int dispatch_frame(Protocol *proto, Socket *_socket, const char *data, uint32_t return SW_ERR; default: - swWarn("unknown opcode [%d]", ws.header.OPCODE); + swoole_warning("unknown opcode [%d]", ws.header.OPCODE); break; } return SW_OK; diff --git a/src/reactor/base.cc b/src/reactor/base.cc index 353363356dd..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,13 +48,13 @@ 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) { - swSysWarn("failed to delete events[fd=%d#%d, type=%d, events=%d]", - _socket->fd, - reactor_->id, - _socket->fd_type, - _socket->events); + swoole_sys_warning("failed to delete events[fd=%d#%d, type=%d, events=%d]", + _socket->fd, + reactor_->id, + _socket->fd_type, + _socket->events); } } @@ -110,11 +110,11 @@ 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); } - set_end_callback(Reactor::PRIORITY_DEFER_TASK, [](Reactor *reactor) { + set_end_callback(PRIORITY_DEFER_TASK, [](Reactor *reactor) { CallbackManager *cm = reactor->defer_tasks; if (cm) { reactor->defer_tasks = nullptr; @@ -123,30 +123,30 @@ Reactor::Reactor(int max_event, Type _type) { } }); - set_exit_condition(Reactor::EXIT_CONDITION_DEFER_TASK, - [](Reactor *reactor, int &event_num) -> bool { return reactor->defer_tasks == nullptr; }); + set_exit_condition(EXIT_CONDITION_DEFER_TASK, + [](Reactor *reactor, size_t &event_num) -> bool { return reactor->defer_tasks == nullptr; }); - set_end_callback(Reactor::PRIORITY_IDLE_TASK, [](Reactor *reactor) { + set_end_callback(PRIORITY_IDLE_TASK, [](Reactor *reactor) { if (reactor->idle_task.callback) { reactor->idle_task.callback(reactor->idle_task.data); } }); - set_end_callback(Reactor::PRIORITY_SIGNAL_CALLBACK, [](Reactor *reactor) { + set_end_callback(PRIORITY_SIGNAL_CALLBACK, [](Reactor *reactor) { if (sw_unlikely(reactor->singal_no)) { - swSignal_callback(reactor->singal_no); + swoole_signal_callback(reactor->singal_no); reactor->singal_no = 0; } }); - set_end_callback(Reactor::PRIORITY_TRY_EXIT, [](Reactor *reactor) { + set_end_callback(PRIORITY_TRY_EXIT, [](Reactor *reactor) { if (reactor->wait_exit && reactor->if_exit()) { reactor->running = false; } }); #ifdef SW_USE_MALLOC_TRIM - set_end_callback(Reactor::PRIORITY_MALLOC_TRIM, [](Reactor *reactor) { + set_end_callback(PRIORITY_MALLOC_TRIM, [](Reactor *reactor) { time_t now = ::time(nullptr); if (reactor->last_malloc_trim_time < now - SW_MALLOC_TRIM_INTERVAL) { malloc_trim(SW_MALLOC_TRIM_PAD); @@ -155,26 +155,26 @@ Reactor::Reactor(int max_event, Type _type) { }); #endif - set_exit_condition(Reactor::EXIT_CONDITION_DEFAULT, - [](Reactor *reactor, int &event_num) -> bool { return event_num == 0; }); + set_exit_condition(EXIT_CONDITION_DEFAULT, + [](Reactor *reactor, size_t &event_num) -> bool { return event_num == 0; }); } bool Reactor::set_handler(int _fdtype, ReactorHandler handler) { int fdtype = get_fd_type(_fdtype); if (fdtype >= SW_MAX_FDTYPE) { - swWarn("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE); + swoole_warning("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE); return false; } - if (Reactor::isset_read_event(_fdtype)) { + if (isset_read_event(_fdtype)) { read_handler[fdtype] = handler; - } else if (Reactor::isset_write_event(_fdtype)) { + } else if (isset_write_event(_fdtype)) { write_handler[fdtype] = handler; - } else if (Reactor::isset_error_event(_fdtype)) { + } else if (isset_error_event(_fdtype)) { error_handler[fdtype] = handler; } else { - swWarn("unknown fdtype"); + swoole_warning("unknown fdtype"); return false; } @@ -182,7 +182,7 @@ bool Reactor::set_handler(int _fdtype, ReactorHandler handler) { } bool Reactor::if_exit() { - int _event_num = event_num; + size_t _event_num = get_event_num(); for (auto &kv : exit_conditions) { if (kv.second(this, _event_num) == false) { return false; @@ -202,26 +202,15 @@ static void reactor_begin(Reactor *reactor) { } int Reactor::_close(Reactor *reactor, Socket *socket) { - if (socket->out_buffer) { - delete socket->out_buffer; - socket->out_buffer = nullptr; - } - if (socket->in_buffer) { - delete socket->in_buffer; - socket->in_buffer = nullptr; - } - - swTraceLog(SW_TRACE_CLOSE, "fd=%d", socket->fd); - + swoole_trace_log(SW_TRACE_CLOSE, "fd=%d", socket->fd); socket->free(); - return SW_OK; } using SendFunc = std::function; using AppendFunc = std::function; -static int write_func( +static ssize_t write_func( Reactor *reactor, Socket *socket, const size_t __len, const SendFunc &send_fn, const AppendFunc &append_fn) { ssize_t retval; Buffer *buffer = socket->out_buffer; @@ -257,12 +246,12 @@ static int 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); if (!buffer) { - swWarn("create worker buffer failed"); + swoole_warning("create worker buffer failed"); return SW_ERR; } socket->out_buffer = buffer; @@ -292,10 +281,10 @@ static int write_func( } append_fn(buffer); } - return SW_OK; + return __len; } -int Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_t n) { +ssize_t Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_t n) { ssize_t send_bytes = 0; auto send_fn = [&send_bytes, socket, buf, n]() -> ssize_t { send_bytes = socket->send(buf, n, 0); @@ -308,7 +297,7 @@ int Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_t n) return write_func(reactor, socket, n, send_fn, append_fn); } -int 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"); @@ -370,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); @@ -394,7 +383,11 @@ void Reactor::set_end_callback(enum EndCallback id, const std::function &fn) { +/** + * Returns false, the reactor cannot be exited, the next condition is skipped + * Returns true, the reactor can exit and will continue to execute the next conditional function + */ +void Reactor::set_exit_condition(enum ExitCondition id, const std::function &fn) { exit_conditions[id] = fn; } @@ -415,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 fd0d440d983..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 | +----------------------------------------------------------------------+ */ @@ -18,8 +18,6 @@ #include "swoole_socket.h" #include "swoole_reactor.h" -#define EVENT_DEBUG 0 - #ifdef HAVE_EPOLL #include #ifndef EPOLLRDHUP @@ -66,15 +64,6 @@ class ReactorEpoll : public ReactorImpl { } }; -#if EVENT_DEBUG -#include -static thread_local std::unordered_map event_map; - -Socket *swoole_event_map_get(int sockfd) { - return event_map[sockfd]; -} -#endif - ReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events) { return new ReactorEpoll(_reactor, max_events); } @@ -82,7 +71,7 @@ ReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events) { ReactorEpoll::ReactorEpoll(Reactor *_reactor, int max_events) : ReactorImpl(_reactor) { epfd_ = epoll_create(512); if (!ready()) { - swSysWarn("epoll_create failed"); + swoole_sys_warning("epoll_create failed"); return; } @@ -109,17 +98,13 @@ int ReactorEpoll::add(Socket *socket, int events) { e.data.ptr = socket; if (epoll_ctl(epfd_, EPOLL_CTL_ADD, socket->fd, &e) < 0) { - swSysWarn( + swoole_sys_warning( "failed to add events[fd=%d#%d, type=%d, events=%d]", socket->fd, reactor_->id, socket->fd_type, events); return SW_ERR; } -#if EVENT_DEBUG - event_map[socket->fd] = socket; -#endif - reactor_->_add(socket, events); - swTraceLog( + swoole_trace_log( SW_TRACE_EVENT, "add events[fd=%d#%d, type=%d, events=%d]", socket->fd, reactor_->id, socket->fd_type, events); return SW_OK; @@ -129,8 +114,8 @@ int ReactorEpoll::del(Socket *_socket) { if (_socket->removed) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_EVENT_SOCKET_REMOVED, - "failed to delete events[%d], it has already been removed", - _socket->fd); + "failed to delete events[fd=%d, fd_type=%d], it has already been removed", + _socket->fd, _socket->fd_type); return SW_ERR; } if (epoll_ctl(epfd_, EPOLL_CTL_DEL, _socket->fd, nullptr) < 0) { @@ -140,11 +125,7 @@ int ReactorEpoll::del(Socket *_socket) { } } -#if EVENT_DEBUG - event_map.erase(_socket->fd); -#endif - - swTraceLog(SW_TRACE_REACTOR, "remove event[reactor_id=%d|fd=%d]", reactor_->id, _socket->fd); + swoole_trace_log(SW_TRACE_REACTOR, "remove event[reactor_id=%d|fd=%d]", reactor_->id, _socket->fd); reactor_->_del(_socket); return SW_OK; @@ -158,19 +139,19 @@ int ReactorEpoll::set(Socket *socket, int events) { int ret = epoll_ctl(epfd_, EPOLL_CTL_MOD, socket->fd, &e); if (ret < 0) { - swSysWarn( + swoole_sys_warning( "failed to set events[fd=%d#%d, type=%d, events=%d]", socket->fd, reactor_->id, socket->fd_type, events); return SW_ERR; } - swTraceLog(SW_TRACE_EVENT, "set event[reactor_id=%d, fd=%d, events=%d]", reactor_->id, socket->fd, events); + swoole_trace_log(SW_TRACE_EVENT, "set event[reactor_id=%d, fd=%d, events=%d]", reactor_->id, socket->fd, events); reactor_->_set(socket, events); return SW_OK; } int ReactorEpoll::wait(struct timeval *timeo) { - swEvent event; + Event event; ReactorHandler handler; int i, n, ret; @@ -194,7 +175,7 @@ int ReactorEpoll::wait(struct timeval *timeo) { n = epoll_wait(epfd_, events_, max_event_num, reactor_->get_timeout_msec()); if (n < 0) { if (!reactor_->catch_error()) { - swSysWarn("[Reactor#%d] epoll_wait failed", reactor_id); + swoole_sys_warning("[Reactor#%d] epoll_wait failed", reactor_id); return SW_ERR; } else { goto _continue; @@ -217,7 +198,7 @@ int ReactorEpoll::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_READ, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("EPOLLIN handle failed. fd=%d", event.fd); + swoole_sys_warning("EPOLLIN handle failed. fd=%d", event.fd); } } // write @@ -225,7 +206,7 @@ int ReactorEpoll::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("EPOLLOUT handle failed. fd=%d", event.fd); + swoole_sys_warning("EPOLLOUT handle failed. fd=%d", event.fd); } } // error @@ -237,7 +218,7 @@ int ReactorEpoll::wait(struct timeval *timeo) { handler = reactor_->get_error_handler(event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("EPOLLERR handle failed. fd=%d", event.fd); + swoole_sys_warning("EPOLLERR handle failed. fd=%d", event.fd); } } if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { diff --git a/src/reactor/kqueue.cc b/src/reactor/kqueue.cc index 2b47b77db66..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 { @@ -71,7 +74,7 @@ ReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events) { ReactorKqueue::ReactorKqueue(Reactor *reactor, int max_events) : ReactorImpl(reactor) { epfd_ = kqueue(); if (epfd_ < 0) { - swWarn("[swReactorKqueueCreate] kqueue_create[0] fail"); + swoole_warning("[swReactorKqueueCreate] kqueue_create[0] fail"); return; } @@ -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); @@ -112,7 +115,7 @@ int ReactorKqueue::add(Socket *socket, int events) { EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn( + swoole_sys_warning( "add events_[fd=%d, reactor_id=%d, type=%d, events=read] failed", fd, reactor_->id, socket->fd_type); return SW_ERR; } @@ -122,14 +125,14 @@ int ReactorKqueue::add(Socket *socket, int events) { EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn( + swoole_sys_warning( "add events_[fd=%d, reactor_id=%d, type=%d, events=write] failed", fd, reactor_->id, socket->fd_type); return SW_ERR; } } reactor_->_add(socket, events); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d, events=%d", SwooleTG.id, epfd_, fd, socket->events); + swoole_trace_log(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d, events=%d", SwooleTG.id, epfd_, fd, socket->events); return SW_OK; } @@ -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); @@ -154,14 +157,14 @@ int ReactorKqueue::set(Socket *socket, int events) { EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn("kqueue->set(%d, SW_EVENT_READ) failed", fd); + swoole_sys_warning("kqueue->set(%d, SW_EVENT_READ) failed", fd); return SW_ERR; } } else { EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn("kqueue->del(%d, SW_EVENT_READ) failed", fd); + swoole_sys_warning("kqueue->del(%d, SW_EVENT_READ) failed", fd); return SW_ERR; } } @@ -170,20 +173,20 @@ int ReactorKqueue::set(Socket *socket, int events) { EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn("kqueue->set(%d, SW_EVENT_WRITE) failed", fd); + swoole_sys_warning("kqueue->set(%d, SW_EVENT_WRITE) failed", fd); return SW_ERR; } } else { EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn("kqueue->del(%d, SW_EVENT_WRITE) failed", fd); + swoole_sys_warning("kqueue->del(%d, SW_EVENT_WRITE) failed", fd); return SW_ERR; } } reactor_->_set(socket, events); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d, events=%d", SwooleTG.id, epfd_, fd, socket->events); + swoole_trace_log(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d, events=%d", SwooleTG.id, epfd_, fd, socket->events); return SW_OK; } @@ -193,13 +196,12 @@ 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); #endif - if (socket->removed) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_EVENT_SOCKET_REMOVED, "failed to delete event[%d], has been removed", socket->fd); @@ -210,7 +212,7 @@ int ReactorKqueue::del(Socket *socket) { EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { - swSysWarn("kqueue->del(%d, SW_EVENT_READ) failed", fd); + swoole_sys_warning("kqueue->del(%d, SW_EVENT_READ) failed", fd); if (errno != EBADF && errno != ENOENT) { return SW_ERR; } @@ -229,7 +231,7 @@ int ReactorKqueue::del(Socket *socket) { } reactor_->_del(socket); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d", SwooleTG.id, epfd_, fd); + swoole_trace_log(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d", SwooleTG.id, epfd_, fd); return SW_OK; } @@ -271,7 +273,7 @@ int ReactorKqueue::wait(struct timeval *timeo) { n = ::kevent(epfd_, nullptr, 0, events_, event_max_, t_ptr); if (n < 0) { if (!reactor_->catch_error()) { - swWarn("kqueue[#%d], epfd=%d", reactor_->id, epfd_); + swoole_warning("kqueue[#%d], epfd=%d", reactor_->id, epfd_); return SW_ERR; } else { goto _continue; @@ -281,7 +283,7 @@ int ReactorKqueue::wait(struct timeval *timeo) { SW_REACTOR_CONTINUE; } - swTraceLog(SW_TRACE_EVENT, "n %d events", n); + swoole_trace_log(SW_TRACE_EVENT, "n %d events", n); for (i = 0; i < n; i++) { struct kevent *kevent = &events_[i]; @@ -296,7 +298,7 @@ int ReactorKqueue::wait(struct timeval *timeo) { handler = reactor_->get_handler(kevent->filter == EVFILT_READ ? SW_EVENT_READ : SW_EVENT_WRITE, event.type); if (sw_unlikely(handler(reactor_, &event) < 0)) { - swSysWarn("kqueue event %s socket#%d handler failed", + swoole_sys_warning("kqueue event %s socket#%d handler failed", kevent->filter == EVFILT_READ ? "read" : "write", event.fd); } @@ -305,27 +307,21 @@ int ReactorKqueue::wait(struct timeval *timeo) { break; } case EVFILT_SIGNAL: { - struct sw_signal { - swSignalHandler handler; - uint16_t signo; - uint16_t active; - }; - struct sw_signal *sw_signal = (struct sw_signal *) udata; - - if (sw_signal->active) { - if (sw_signal->handler) { - sw_signal->handler(sw_signal->signo); + Signal *signal_data = (Signal *) udata; + if (signal_data->activated) { + if (signal_data->handler) { + signal_data->handler(signal_data->signo); } else { swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, - swSignal_str(sw_signal->signo)); + swoole_signal_to_str(signal_data->signo)); } } break; } default: - swWarn("unknown event filter[%d]", kevent->filter); + swoole_warning("unknown event filter[%d]", kevent->filter); break; } } diff --git a/src/reactor/poll.cc b/src/reactor/poll.cc index 9b46db01311..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 | +----------------------------------------------------------------------+ */ @@ -62,19 +62,19 @@ ReactorPoll::~ReactorPoll() { int ReactorPoll::add(Socket *socket, int events) { int fd = socket->fd; if (exists(fd)) { - swWarn("fd#%d is already exists", fd); + swoole_warning("fd#%d is already exists", fd); return SW_ERR; } - int cur = reactor_->event_num; - if (reactor_->event_num == max_fd_num) { - swWarn("too many connection, more than %d", max_fd_num); + int cur = reactor_->get_event_num(); + if (reactor_->get_event_num() == max_fd_num) { + swoole_warning("too many connection, more than %d", max_fd_num); return SW_ERR; } reactor_->_add(socket, events); - swTrace("fd=%d, events=%d", fd, events); + swoole_trace("fd=%d, events=%d", fd, events); fds_[cur] = socket; events_[cur].fd = fd; @@ -96,9 +96,9 @@ int ReactorPoll::add(Socket *socket, int events) { int ReactorPoll::set(Socket *socket, int events) { uint32_t i; - swTrace("fd=%d, events=%d", socket->fd, events); + swoole_trace("fd=%d, events=%d", socket->fd, events); - for (i = 0; i < reactor_->event_num; i++) { + for (i = 0; i < reactor_->get_event_num(); i++) { // found if (events_[i].fd == socket->fd) { events_[i].events = 0; @@ -126,10 +126,10 @@ int ReactorPoll::del(Socket *socket) { return SW_ERR; } - for (uint32_t i = 0; i < reactor_->event_num; i++) { + for (uint32_t i = 0; i < reactor_->get_event_num(); i++) { if (events_[i].fd == socket->fd) { - for (; i < reactor_->event_num; i++) { - if (i == reactor_->event_num) { + for (; i < reactor_->get_event_num(); i++) { + if (i == reactor_->get_event_num()) { fds_[i] = nullptr; events_[i].fd = 0; events_[i].events = 0; @@ -166,10 +166,10 @@ int ReactorPoll::wait(struct timeval *timeo) { if (reactor_->onBegin != nullptr) { reactor_->onBegin(reactor_); } - ret = poll(events_, reactor_->event_num, reactor_->get_timeout_msec()); + ret = poll(events_, reactor_->get_event_num(), reactor_->get_timeout_msec()); if (ret < 0) { if (!reactor_->catch_error()) { - swSysWarn("poll error"); + swoole_sys_warning("poll error"); break; } else { goto _continue; @@ -178,7 +178,7 @@ int ReactorPoll::wait(struct timeval *timeo) { reactor_->execute_end_callbacks(true); SW_REACTOR_CONTINUE; } else { - for (uint32_t i = 0; i < reactor_->event_num; i++) { + for (uint32_t i = 0; i < reactor_->get_event_num(); i++) { event.socket = fds_[i]; event.fd = events_[i].fd; event.reactor_id = reactor_->id; @@ -188,13 +188,13 @@ int ReactorPoll::wait(struct timeval *timeo) { event.socket->event_hup = 1; } - swTrace("Event: fd=%d|reactor_id=%d|type=%d", event.fd, reactor_->id, event.type); + swoole_trace("Event: fd=%d|reactor_id=%d|type=%d", event.fd, reactor_->id, event.type); // in if ((events_[i].revents & POLLIN) && !event.socket->removed) { handler = reactor_->get_handler(SW_EVENT_READ, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("poll[POLLIN] handler failed. fd=%d", event.fd); + swoole_sys_warning("poll[POLLIN] handler failed. fd=%d", event.fd); } } // out @@ -202,7 +202,7 @@ int ReactorPoll::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("poll[POLLOUT] handler failed. fd=%d", event.fd); + swoole_sys_warning("poll[POLLOUT] handler failed. fd=%d", event.fd); } } // error @@ -214,7 +214,7 @@ int ReactorPoll::wait(struct timeval *timeo) { handler = reactor_->get_error_handler(event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("poll[POLLERR] handler failed. fd=%d", event.fd); + swoole_sys_warning("poll[POLLERR] handler failed. fd=%d", event.fd); } } if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { @@ -230,7 +230,7 @@ int ReactorPoll::wait(struct timeval *timeo) { } bool ReactorPoll::exists(int fd) { - for (uint32_t i = 0; i < reactor_->event_num; i++) { + for (uint32_t i = 0; i < reactor_->get_event_num(); i++) { if (events_[i].fd == fd) { return true; } diff --git a/src/reactor/select.cc b/src/reactor/select.cc index 2cad587a1c6..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 | +----------------------------------------------------------------------+ */ @@ -67,7 +67,7 @@ ReactorSelect::ReactorSelect(Reactor *reactor) : ReactorImpl(reactor) { int ReactorSelect::add(Socket *socket, int events) { int fd = socket->fd; if (fd > FD_SETSIZE) { - swWarn("max fd value is FD_SETSIZE(%d).\n", FD_SETSIZE); + swoole_warning("max fd value is FD_SETSIZE(%d).\n", FD_SETSIZE); return SW_ERR; } @@ -90,7 +90,7 @@ int ReactorSelect::del(Socket *socket) { } int fd = socket->fd; if (fds.erase(fd) == 0) { - swWarn("swReactorSelect: fd[%d] not found", fd); + swoole_warning("swReactorSelect: fd[%d] not found", fd); return SW_ERR; } SW_FD_CLR(fd, &rfds); @@ -103,7 +103,7 @@ int ReactorSelect::del(Socket *socket) { int ReactorSelect::set(Socket *socket, int events) { auto i = fds.find(socket->fd); if (i == fds.end()) { - swWarn("swReactorSelect: sock[%d] not found", socket->fd); + swoole_warning("swReactorSelect: sock[%d] not found", socket->fd); return SW_ERR; } reactor_->_set(socket, events); @@ -163,7 +163,7 @@ int ReactorSelect::wait(struct timeval *timeo) { ret = select(maxfd + 1, &(rfds), &(wfds), &(efds), &timeout); if (ret < 0) { if (!reactor_->catch_error()) { - swSysWarn("select error"); + swoole_sys_warning("select error"); break; } else { goto _continue; @@ -187,7 +187,7 @@ int ReactorSelect::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_READ, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("[Reactor#%d] select event[type=READ, fd=%d] handler fail", reactor_->id, event.fd); + swoole_sys_warning("[Reactor#%d] select event[type=READ, fd=%d] handler fail", reactor_->id, event.fd); } } // write @@ -195,7 +195,7 @@ int ReactorSelect::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("[Reactor#%d] select event[type=WRITE, fd=%d] handler fail", reactor_->id, event.fd); + swoole_sys_warning("[Reactor#%d] select event[type=WRITE, fd=%d] handler fail", reactor_->id, event.fd); } } // error @@ -203,7 +203,7 @@ int ReactorSelect::wait(struct timeval *timeo) { handler = reactor_->get_handler(SW_EVENT_ERROR, event.type); ret = handler(reactor_, &event); if (ret < 0) { - swSysWarn("[Reactor#%d] select event[type=ERROR, fd=%d] handler fail", reactor_->id, event.fd); + swoole_sys_warning("[Reactor#%d] select event[type=ERROR, fd=%d] handler fail", reactor_->id, event.fd); } } if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { diff --git a/src/server/base.cc b/src/server/base.cc index 33245e01bd3..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; } @@ -28,43 +64,37 @@ bool BaseFactory::shutdown() { } bool BaseFactory::dispatch(SendData *task) { - PacketPtr pkg{}; Connection *conn = nullptr; if (Server::is_stream_event(task->info.type)) { conn = server_->get_connection(task->info.fd); if (conn == nullptr || conn->active == 0) { - swWarn("dispatch[type=%d] failed, socket#%ld is not active", task->info.type, task->info.fd); + swoole_warning("dispatch[type=%d] failed, socket#%ld is not active", task->info.type, task->info.fd); return false; } // server active close, discard data. if (conn->closed) { - swWarn("dispatch[type=%d] failed, socket#%ld is closed by server", task->info.type, task->info.fd); + swoole_warning("dispatch[type=%d] failed, socket#%ld is closed by server", task->info.type, task->info.fd); return false; } // converted fd to session_id task->info.fd = conn->session_id; task->info.server_fd = conn->server_fd; } - // with data - if (task->info.len > 0) { - memcpy(&pkg.info, &task->info, sizeof(pkg.info)); - pkg.info.flags = SW_EVENT_DATA_PTR; - pkg.data.length = task->info.len; - pkg.data.str = (char *) task->data; + if (task->info.len > 0) { if (conn && conn->socket->recv_buffer && task->data == conn->socket->recv_buffer->str && conn->socket->recv_buffer->offset > 0 && conn->socket->recv_buffer->length == (size_t) conn->socket->recv_buffer->offset) { - pkg.info.flags |= SW_EVENT_DATA_POP_PTR; + task->info.flags |= SW_EVENT_DATA_POP_PTR; } - - return server_->accept_task((EventData *) &pkg) == SW_OK; - } - // no data - else { - return server_->accept_task((EventData *) &task->info) == SW_OK; } + + auto bus = server_->get_worker_message_bus(); + bus->pass(task); + server_->worker_accept_event(&bus->get_buffer()->info); + + return true; } /** @@ -73,12 +103,12 @@ bool BaseFactory::dispatch(SendData *task) { bool BaseFactory::notify(DataHead *info) { Connection *conn = server_->get_connection(info->fd); if (conn == nullptr || conn->active == 0) { - swWarn("dispatch[type=%d] failed, socket#%ld is not active", info->type, info->fd); + swoole_warning("dispatch[type=%d] failed, socket#%ld is not active", info->type, info->fd); return false; } // server active close, discard data. if (conn->closed) { - swWarn("dispatch[type=%d] failed, session#%ld is closed by server", info->type, conn->session_id); + swoole_warning("dispatch[type=%d] failed, session#%ld is closed by server", info->type, conn->session_id); return false; } // converted fd to session_id @@ -86,7 +116,9 @@ bool BaseFactory::notify(DataHead *info) { info->server_fd = conn->server_fd; info->flags = SW_EVENT_DATA_NORMAL; - return server_->accept_task((EventData *) info) == SW_OK; + server_->worker_accept_event(info); + + return true; } bool BaseFactory::end(SessionId session_id, int flags) { @@ -94,29 +126,31 @@ 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, SW_ERROR_SESSION_NOT_EXIST, - "failed to close connection, session#%ld does not exist", session_id); + 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) { - swSysWarn("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); if (conn == nullptr) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); return false; - } + } // Reset send buffer, Immediately close the connection. if (flags & Server::CLOSE_RESET) { conn->close_reset = 1; @@ -125,45 +159,44 @@ bool BaseFactory::end(SessionId session_id, int flags) { if (flags & Server::CLOSE_ACTIVELY) { conn->close_actively = 1; } - if (conn->close_force) { - goto _do_close; - } else if (conn->closing) { - swWarn("session#%ld is closing", session_id); + + if (conn->closing) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; - } else if (conn->closed) { + } else if (!(conn->close_force || conn->close_reset) && conn->closed) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; - } else { - _do_close: - conn->closing = 1; - if (server_->onClose != nullptr) { - DataHead info{}; - info.fd = session_id; - if (conn->close_actively) { - info.reactor_id = -1; - } else { - info.reactor_id = conn->reactor_id; - } - info.server_fd = conn->server_fd; - server_->onClose(server_, &info); - } - conn->closing = 0; - conn->closed = 1; - conn->close_errno = 0; - - if (conn->socket == nullptr) { - swWarn("session#%ld->socket is nullptr", session_id); - return false; - } + } - if (Buffer::empty(conn->socket->out_buffer) || conn->peer_closed || conn->close_force) { - Reactor *reactor = SwooleTG.reactor; - return Server::close_connection(reactor, conn->socket) == SW_OK; + conn->closing = 1; + if (server_->onClose != nullptr && !conn->closed) { + DataHead info{}; + info.fd = session_id; + if (conn->close_actively) { + info.reactor_id = -1; } else { - BufferChunk *chunk = conn->socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); - chunk->value.data.val1 = _send.info.type; - conn->close_queued = 1; - return true; + info.reactor_id = conn->reactor_id; } + info.server_fd = conn->server_fd; + server_->onClose(server_, &info); + } + conn->closing = 0; + conn->closed = 1; + conn->close_errno = 0; + network::Socket *_socket = conn->socket; + + if (_socket == nullptr) { + swoole_warning("session#%ld->socket is nullptr", session_id); + return false; + } + + if (Buffer::empty(_socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) { + Reactor *reactor = SwooleTG.reactor; + return Server::close_connection(reactor, _socket) == SW_OK; + } else { + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); + conn->close_queued = 1; + return true; } } @@ -171,51 +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) { - swTrace("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_RECV_DATA) { - proxy_msg.info.fd = session_id; - proxy_msg.info.reactor_id = SwooleG.process_id; - proxy_msg.info.type = SW_SERVER_EVENT_PROXY_START; - - size_t send_n = data->info.len; - size_t offset = 0; - - while (send_n > 0) { - if (send_n > SW_IPC_BUFFER_SIZE) { - proxy_msg.info.len = SW_IPC_BUFFER_SIZE; - } else { - proxy_msg.info.type = SW_SERVER_EVENT_PROXY_END; - proxy_msg.info.len = send_n; - } - memcpy(proxy_msg.data, data->data + offset, proxy_msg.info.len); - send_n -= proxy_msg.info.len; - offset += proxy_msg.info.len; - size_t __len = sizeof(proxy_msg.info) + proxy_msg.info.len; - if (worker->pipe_master->send_async((const char*) &proxy_msg, __len) < 0) { - swSysWarn("failed to send %lu bytes to pipe_master", __len); - return false; - } - } - swTrace("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 { - swWarn("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 8ef9f22c70c..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 | +----------------------------------------------------------------------+ */ @@ -31,22 +31,18 @@ namespace swoole { using ReloadWorkerList = std::unordered_map; struct Manager { - bool reloading; bool reload_all_worker; bool reload_task_worker; - bool reload_init; - bool read_message; bool force_kill; - uint32_t reload_worker_i; uint32_t reload_worker_num; pid_t reload_worker_pid; - Worker *reload_workers; Server *server_; 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); @@ -55,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); } } @@ -70,7 +66,7 @@ void Manager::kill_timeout_process(Timer *timer, TimerNode *tnode) { continue; } if (swoole_kill(pid, SIGKILL) < 0) { - swSysWarn("kill(%d, SIGKILL) [%u] failed", pid, worker_id); + swoole_sys_warning("kill(%d, SIGKILL) [%u] failed", pid, worker_id); } else { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, @@ -101,155 +97,106 @@ 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 == SW_TASK_IPC_UNIXSOCK) { - store_pipe_fd(worker->pipe_object); - } - } + SW_LOOP_N(worker_num) { + create_worker(get_worker(i)); } - // User Worker Process - if (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; } - message_box = Channel::make(65536, sizeof(WorkerStopMessage), SW_CHAN_LOCK | SW_CHAN_SHM); - if (message_box == nullptr) { + 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 - SW_START_SLEEP; - if (!is_started()) { - swError("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) { - swError("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) { - swError("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) { - for (auto worker : *user_worker_list) { - if (worker->pipe_object) { - store_pipe_fd(worker->pipe_object); - } - pid = spawn_user_worker(worker); - if (pid < 0) { - swError("failed to start user workers"); - return SW_ERR; + if (!user_worker_list.empty()) { + for (auto worker : user_worker_list) { + 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->manager_pid = pid; - break; - case -1: - swError("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) { - swWarn("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 ? "\n" SWOOLE_BUG_REPORT : ""); - 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; - SwooleG.use_signalfd = 0; + ProcessPool *pool = &server_->gs->event_workers; + pool->onWorkerMessage = Server::read_worker_message; + _server->gs->manager_pid = _server->gs->event_workers.master_pid = getpid(); + SwooleTG.reactor = nullptr; - reload_workers = new Worker[_server->worker_num + _server->task_worker_num]; + pool->reload_workers = new Worker[_server->worker_num + _server->task_worker_num]; ON_SCOPE_EXIT { - delete[] reload_workers; - reload_workers = nullptr; + delete[] pool->reload_workers; + pool->reload_workers = nullptr; server_->manager = nullptr; }; // for reload - swSignal_set(SIGHUP, nullptr); - swSignal_set(SIGCHLD, signal_handler); - swSignal_set(SIGTERM, signal_handler); - swSignal_set(SIGUSR1, signal_handler); - swSignal_set(SIGUSR2, signal_handler); - swSignal_set(SIGIO, signal_handler); - swSignal_set(SIGALRM, signal_handler); + swoole_signal_set(SIGHUP, nullptr); + swoole_signal_set(SIGCHLD, signal_handler); + swoole_signal_set(SIGTERM, signal_handler); + swoole_signal_set(SIGUSR1, signal_handler); + swoole_signal_set(SIGUSR2, signal_handler); + swoole_signal_set(SIGIO, signal_handler); + swoole_signal_set(SIGALRM, signal_handler); #ifdef SIGRTMIN - swSignal_set(SIGRTMIN, signal_handler); + 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); + 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); } @@ -263,54 +210,56 @@ void Manager::start(Server *_server) { while (_server->running) { ExitStatus exit_status = wait_process(); - - if (read_message) { - WorkerStopMessage msg; - while (_server->message_box->pop(&msg, sizeof(msg)) > 0) { + const auto errnoAfterWait = errno; + if (pool->read_message) { + EventData msg; + while (pool->pop_message(&msg, sizeof(msg)) > 0) { if (!_server->running) { continue; } - if (msg.worker_id >= _server->worker_num) { - _server->spawn_task_worker(_server->get_worker(msg.worker_id)); + if (msg.info.type != SW_WORKER_MESSAGE_STOP && pool->onWorkerMessage) { + pool->onWorkerMessage(pool, &msg); + continue; + } + WorkerStopMessage worker_stop_msg; + memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); + if (worker_stop_msg.worker_id >= _server->worker_num) { + _server->factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { - Worker *worker = _server->get_worker(msg.worker_id); - pid_t new_pid = _server->spawn_event_worker(worker); - if (new_pid > 0) { - worker->pid = new_pid; - } + Worker *worker = _server->get_worker(worker_stop_msg.worker_id); + _server->factory->spawn_event_worker(worker); } } - read_message = false; + 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 (!reloading) { + if (!pool->reloading) { _error: - if (errno > 0 && errno != EINTR) { - swSysWarn("wait() failed"); + if (errnoAfterWait > 0 && errnoAfterWait != EINTR) { + swoole_sys_warning("wait() failed"); } continue; } // reload task & event workers else if (reload_all_worker) { - swInfo("Server is reloading all workers now"); + swoole_info("Server is reloading all workers now"); if (_server->onBeforeReload != nullptr) { _server->onBeforeReload(_server); } - if (!reload_init) { - reload_init = true; - memcpy(reload_workers, _server->workers, sizeof(Worker) * _server->worker_num); + if (!pool->reload_init) { + pool->reload_init = true; + memcpy(pool->reload_workers, _server->workers, sizeof(Worker) * _server->worker_num); add_timeout_killer(_server->workers, _server->worker_num); reload_worker_num = _server->worker_num; if (_server->task_worker_num > 0) { - memcpy(reload_workers + _server->worker_num, + memcpy(pool->reload_workers + _server->worker_num, _server->gs->task_workers.workers, sizeof(Worker) * _server->task_worker_num); reload_worker_num += _server->task_worker_num; @@ -321,13 +270,14 @@ void Manager::start(Server *_server) { reload_all_worker = false; if (_server->reload_async) { SW_LOOP_N(_server->worker_num) { - if (swoole_kill(reload_workers[i].pid, SIGTERM) < 0) { - swSysWarn("swKill(%d, SIGTERM) [%d] failed", reload_workers[i].pid, i); + if (swoole_kill(pool->reload_workers[i].pid, SIGTERM) < 0) { + swoole_sys_warning( + "failed to kill(%d, SIGTERM) worker#[%d]", pool->reload_workers[i].pid, i); } } - reload_worker_i = _server->worker_num; + pool->reload_worker_i = _server->worker_num; } else { - reload_worker_i = 0; + pool->reload_worker_i = 0; } } goto _kill_worker; @@ -335,21 +285,22 @@ void Manager::start(Server *_server) { // only reload task workers else if (reload_task_worker) { if (_server->task_worker_num == 0) { - swWarn("cannot reload task workers, task workers is not started"); - reloading = false; + swoole_warning("cannot reload task workers, task workers is not started"); + pool->reloading = false; continue; } - swInfo("Server is reloading task workers now"); + swoole_info("Server is reloading task workers now"); if (_server->onBeforeReload != nullptr) { _server->onBeforeReload(_server); } - if (!reload_init) { - memcpy( - reload_workers, _server->gs->task_workers.workers, sizeof(Worker) * _server->task_worker_num); + if (!pool->reload_init) { + memcpy(pool->reload_workers, + _server->gs->task_workers.workers, + sizeof(Worker) * _server->task_worker_num); add_timeout_killer(_server->gs->task_workers.workers, _server->task_worker_num); reload_worker_num = _server->task_worker_num; - reload_worker_i = 0; - reload_init = true; + pool->reload_worker_i = 0; + pool->reload_init = true; reload_task_worker = false; } goto _kill_worker; @@ -360,62 +311,60 @@ 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 - if (_server->user_worker_map != nullptr) { + if (!_server->user_worker_map.empty()) { Server::wait_other_worker(&_server->gs->event_workers, exit_status); } - if (exit_status.get_pid() == reload_worker_pid && reloading) { - reload_worker_i++; + if (exit_status.get_pid() == reload_worker_pid && pool->reloading) { + pool->reload_worker_i++; } } // reload worker _kill_worker: - if (reloading) { + if (pool->reloading) { // reload finish - if (reload_worker_i >= reload_worker_num) { - reload_worker_pid = reload_worker_i = 0; - reload_init = reloading = false; + if (pool->reload_worker_i >= reload_worker_num) { + reload_worker_pid = pool->reload_worker_i = 0; + pool->reload_init = pool->reloading = false; if (_server->onAfterReload != nullptr) { _server->onAfterReload(_server); } continue; } - reload_worker_pid = reload_workers[reload_worker_i].pid; + reload_worker_pid = pool->reload_workers[pool->reload_worker_i].pid; if (swoole_kill(reload_worker_pid, SIGTERM) < 0) { if (errno == ECHILD || errno == ESRCH) { - reload_worker_i++; + pool->reload_worker_i++; goto _kill_worker; } - swSysWarn("swKill(%d, SIGTERM) [%d] failed", reload_workers[reload_worker_i].pid, reload_worker_i); + swoole_sys_warning("kill(%d, SIGTERM) [%d] failed", + pool->reload_workers[pool->reload_worker_i].pid, + pool->reload_worker_i); } } } @@ -434,8 +383,8 @@ void Manager::start(Server *_server) { kill_workers.push_back(_server->gs->task_workers.workers[i].pid); } } - if (_server->user_worker_map) { - for (auto kv : *_server->user_worker_map) { + if (!_server->user_worker_map.empty()) { + for (auto &kv : _server->user_worker_map) { kill_workers.push_back(kv.second->pid); } } @@ -444,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); @@ -456,52 +405,42 @@ 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; } 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 (!manager->reloading) { - manager->reloading = true; - manager->reload_all_worker = true; - } - sw_logger()->reopen(); - break; - /** - * only reload task workers - */ case SIGUSR2: - if (!manager->reloading) { - manager->reloading = true; - manager->reload_task_worker = true; - } + _server->reload(signo == SIGUSR1); sw_logger()->reopen(); break; case SIGIO: - manager->read_message = true; + 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 @@ -526,9 +465,9 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) break; } } - if (serv->user_worker_map) { - auto iter = serv->user_worker_map->find(exit_status.get_pid()); - if (iter != serv->user_worker_map->end()) { + if (!serv->user_worker_map.empty()) { + auto iter = serv->user_worker_map.find(exit_status.get_pid()); + if (iter != serv->user_worker_map.end()) { worker_type = SW_PROCESS_USERWORKER; exit_worker = iter->second; break; @@ -537,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 */ @@ -557,109 +496,61 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) } /** - * kill and wait all user process + * [manager] */ -void Server::kill_user_workers() { - if (!user_worker_map) { +void Server::read_worker_message(ProcessPool *pool, EventData *msg) { + if (msg->info.type != SW_SERVER_EVENT_COMMAND_REQUEST) { + swoole_warning("unknown worker message type[%d]", msg->info.type); 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) { - swSysWarn("waitpid(%d) failed", kv.second->pid); - } + Server *serv = (Server *) pool->ptr; + int command_id = msg->info.server_fd; + 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[command_id=%d]", command_id); + return; } -} -/** - * kill and wait all child process - */ -void Server::kill_event_workers() { - int status; + Server::Command::Handler handler = iter->second; + auto result = handler(serv, std::string(msg->data, msg->info.len)); - if (worker_num == 0) { - return; - } + SendData task{}; + task.info.fd = msg->info.fd; + task.info.reactor_id = 0; + task.info.server_fd = -1; + task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; + task.info.len = result.length(); + task.data = result.c_str(); - for (uint32_t i = 0; i < worker_num; i++) { - swTrace("[Manager]kill worker processor"); - swoole_kill(workers[i].pid, SIGTERM); - } - for (uint32_t i = 0; i < worker_num; i++) { - if (swoole_waitpid(workers[i].pid, &status, 0) < 0) { - swSysWarn("waitpid(%d) failed", workers[i].pid); - } - } + serv->message_bus.write(serv->get_command_reply_socket(), &task); } -/** - * kill and wait task process - */ -void Server::kill_task_workers() { - if (task_worker_num == 0) { - return; +bool Server::reload(bool reload_all_workers) { + if (is_thread_mode()) { + return reload_worker_threads(reload_all_workers); } - gs->task_workers.shutdown(); -} -pid_t Server::spawn_event_worker(Worker *worker) { - pid_t pid; - - pid = swoole_fork(0); - - // fork() failed - if (pid < 0) { - swSysWarn("Fork Worker failed"); - return SW_ERR; - } - // worker child processor - else if (pid == 0) { - exit(start_event_worker(worker)); - } - // parent,add to writer - else { - return 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; } -} - -pid_t Server::spawn_user_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - if (pid < 0) { - swSysWarn("Fork Worker failed"); - return SW_ERR; + if (getpid() != gs->manager_pid) { + return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) == 0; } - // 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); - } - // 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; + + ProcessPool *pool = &gs->event_workers; + if (!pool->reload()) { + return false; } -} -pid_t Server::spawn_task_worker(Worker *worker) { - return gs->task_workers.spawn(worker); + if (reload_all_workers) { + manager->reload_all_worker = true; + } else { + manager->reload_task_worker = true; + } + return true; } } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index 738cee8b20d..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,21 +79,74 @@ 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=%" PRId64 "]", + request_id); + return; + } + iter->second(this, result); + command_callbacks.erase(request_id); +} + +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; + } + + Server::Command::Handler handler = iter->second; + auto packet = mb.get_packet(); + auto result = handler(this, std::string(packet.data, packet.length)); + + SendData task{}; + task.info.fd = buffer->info.fd; + task.info.reactor_id = worker_id; + task.info.server_fd = -1; + task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; + task.info.len = result.length(); + task.data = result.c_str(); + + mb.write(sock, &task); +} + +std::string Server::call_command_handler_in_master(int command_id, const std::string &msg) { + 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[%d]", command_id); + return ""; + } + + Server::Command::Handler handler = iter->second; + return handler(this, msg); +} + +int Server::accept_command_result(Reactor *reactor, Event *event) { + Server *serv = (Server *) reactor->ptr; + + if (serv->message_bus.read(event->socket) <= 0) { + return SW_OK; } + + auto packet = serv->message_bus.get_packet(); + std::string result(packet.data, packet.length); + + auto buffer = serv->message_bus.get_buffer(); + serv->call_command_callback(buffer->info.fd, result); + serv->message_bus.pop(); + + return SW_OK; } 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(); @@ -109,22 +160,22 @@ int Server::accept_connection(Reactor *reactor, Event *event) { if (errno == EMFILE || errno == ENFILE) { serv->disable_accept(); } - swSysWarn("accept() failed"); + swoole_sys_warning("accept() failed"); return SW_OK; } } - swTrace("[Master] Accept new connection. maxfd=%d|minfd=%d|reactor_id=%d|conn=%d", - serv->get_maxfd(), - serv->get_minfd(), - reactor->id, - sock->fd); + swoole_trace("[Master] Accept new connection. maxfd=%d|minfd=%d|reactor_id=%d|conn=%d", + serv->get_maxfd(), + serv->get_minfd(), + reactor->id, + sock->fd); // too many connection if (sock->fd >= (int) serv->max_connection) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_TOO_MANY_SOCKET, "Too many connections [now: %d]", sock->fd); - sock->free(); + serv->abort_connection(reactor, listen_host, sock); serv->disable_accept(); return SW_OK; } @@ -132,7 +183,7 @@ int Server::accept_connection(Reactor *reactor, Event *event) { // add to connection_list Connection *conn = serv->add_connection(listen_host, sock, event->fd); if (conn == nullptr) { - sock->free(); + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } sock->chunk_size = SW_SEND_BUFFER_SIZE; @@ -140,7 +191,7 @@ int Server::accept_connection(Reactor *reactor, Event *event) { #ifdef SW_USE_OPENSSL if (listen_host->ssl) { if (!listen_host->ssl_create(conn, sock)) { - reactor->close(reactor, sock); + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } } else { @@ -149,7 +200,7 @@ int Server::accept_connection(Reactor *reactor, Event *event) { #endif if (serv->single_thread) { if (serv->connection_incoming(reactor, conn) < 0) { - reactor->close(reactor, sock); + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } } else { @@ -157,8 +208,9 @@ 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) { - reactor->close(reactor, sock); + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } } @@ -172,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) { @@ -220,14 +272,14 @@ dtls::Session *Server::accept_dtls_connection(ListenPort *port, Address *sa) { } if (sock->bind(port->socket->info) < 0) { - swSysWarn("bind() failed"); + swoole_sys_warning("bind() failed"); goto _cleanup; } if (sock->is_inet6()) { sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0); } if (sock->connect(sa) < 0) { - swSysWarn("connect(%s:%d) failed", sa->get_addr(), sa->get_port()); + swoole_sys_warning("connect(%s:%d) failed", sa->get_addr(), sa->get_port()); goto _cleanup; } @@ -262,7 +314,7 @@ dtls::Session *Server::accept_dtls_connection(ListenPort *port, Address *sa) { void Server::set_max_connection(uint32_t _max_connection) { if (connection_list != nullptr) { - swWarn("max_connection must be set before server create"); + swoole_warning("max_connection must be set before server create"); return; } max_connection = _max_connection; @@ -270,12 +322,24 @@ void Server::set_max_connection(uint32_t _max_connection) { max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); } else if (max_connection > SW_SESSION_LIST_SIZE) { max_connection = SW_SESSION_LIST_SIZE; - swWarn("max_connection is exceed the SW_SESSION_LIST_SIZE, it's reset to %u", SW_SESSION_LIST_SIZE); + swoole_warning("max_connection is exceed the SW_SESSION_LIST_SIZE, it's reset to %u", SW_SESSION_LIST_SIZE); } if (SwooleG.max_sockets > 0 && max_connection > SwooleG.max_sockets) { max_connection = SwooleG.max_sockets; - swWarn("max_connection is exceed the maximum value, it's reset to %u", SwooleG.max_sockets); + swoole_warning("max_connection is exceed the maximum value, it's reset to %u", SwooleG.max_sockets); + } +} + +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() { @@ -283,19 +347,31 @@ int Server::start_check() { if (is_process_mode()) { if (!is_support_unsafe_events()) { if (onConnect) { - swWarn("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) { - swWarn("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) { - swWarn("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) { - swWarn("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; @@ -308,7 +384,7 @@ int Server::start_check() { } if (task_worker_num > 0) { if (onTask == nullptr) { - swWarn("onTask event callback must be set"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onTask' callback"); return SW_ERR; } } @@ -327,11 +403,11 @@ int Server::start_check() { ls->protocol.package_max_length = SW_BUFFER_MIN_SIZE; } if (if_require_receive_callback(ls, onReceive != nullptr)) { - swWarn("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)) { - swWarn("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) { @@ -345,14 +421,61 @@ int Server::start_check() { /** * OpenSSL thread-safe */ - if (is_process_mode() && !single_thread) { - swSSL_init_thread_safety(); + if ((is_process_mode() && !single_thread) || is_thread_mode()) { + swoole_ssl_init_thread_safety(); } #endif return SW_OK; } +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(); + swoole_set_process_type(SW_PROCESS_MASTER); + + reactor->ptr = this; + reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); + + if (pipe_command) { + if (!single_thread) { + reactor->set_handler(SW_FD_PIPE, Server::accept_command_result); + } + reactor->add(pipe_command->get_socket(true), SW_EVENT_READ); + } + + if ((master_timer = swoole_timer_add(1000L, true, Server::timer_callback, this)) == nullptr) { + swoole_event_free(); + return SW_ERR; + } + + if (!single_thread && !is_thread_mode()) { + reactor_thread_barrier.wait(); + } + if (is_process_mode()) { + gs->manager_barrier.wait(); + } + gs->master_pid = getpid(); + + if (isset_hook(HOOK_MASTER_START)) { + call_hook(HOOK_MASTER_START, this); + } + + if (onStart) { + onStart(this); + } + + return swoole_event_wait(); +} + void Server::store_listen_socket() { for (auto ls : ports) { int sockfd = ls->socket->fd; @@ -362,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); @@ -374,12 +498,12 @@ void Server::store_listen_socket() { */ int Server::create_task_workers() { key_t key = 0; - swIPC_type ipc_mode; + swIPCMode ipc_mode; - if (task_ipc_mode == SW_TASK_IPC_MSGQUEUE || task_ipc_mode == SW_TASK_IPC_PREEMPTIVE) { + if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) { key = message_queue_key; ipc_mode = SW_IPC_MSGQUEUE; - } else if (task_ipc_mode == SW_TASK_IPC_STREAM) { + } else if (task_ipc_mode == TASK_IPC_STREAM) { ipc_mode = SW_IPC_SOCKET; } else { ipc_mode = SW_IPC_UNIXSOCK; @@ -388,7 +512,7 @@ int Server::create_task_workers() { ProcessPool *pool = &gs->task_workers; *pool = {}; if (pool->create(task_worker_num, key, ipc_mode) < 0) { - swWarn("[Master] create task_workers failed"); + swoole_warning("[Master] create task_workers failed"); return SW_ERR; } @@ -404,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; @@ -413,24 +555,22 @@ 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() { - /** - * if Swoole\Server::addProcess is called first, - * Server::user_worker_list is initialized in the Server_add_worker function - */ - if (user_worker_list == nullptr) { - user_worker_list = new std::vector; - } - - user_workers = (Worker *) sw_shm_calloc(user_worker_num, sizeof(Worker)); + user_workers = (Worker *) sw_shm_calloc(get_user_worker_num(), sizeof(Worker)); if (user_workers == nullptr) { - swSysWarn("gmalloc[server->user_workers] failed"); + 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; } @@ -439,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) { @@ -449,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) { - swSysWarn("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. @@ -524,7 +627,7 @@ int Server::start() { if (null_fd > 0) { swoole_redirect_stdout(null_fd); } else { - swSysWarn("open(/dev/null) failed"); + swoole_sys_warning("open(/dev/null) failed"); } } @@ -533,8 +636,6 @@ int Server::start() { } } - // master pid - gs->master_pid = getpid(); gs->start_time = ::time(nullptr); /** @@ -545,50 +646,25 @@ int Server::start() { gs->event_workers.worker_num = worker_num; gs->event_workers.use_msgqueue = 0; - uint32_t i; - for (i = 0; i < worker_num; i++) { + SW_LOOP_N(worker_num) { gs->event_workers.workers[i].pool = &gs->event_workers; gs->event_workers.workers[i].id = i; 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) { - swWarn("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); - } - } - - /** - * user worker process - */ - if (user_worker_list) { - i = 0; - for (auto worker : *user_worker_list) { + if (!user_worker_list.empty()) { + uint32_t i = 0; + for (auto worker : user_worker_list) { worker->id = worker_num + task_worker_num + i; i++; } } + running = true; // factory 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()); @@ -597,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) { @@ -616,41 +697,28 @@ int Server::start() { * initializing server config, set default */ Server::Server(enum Mode _mode) { - swoole_init(); - - reactor_num = SW_REACTOR_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_REACTOR_NUM; - + 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; - factory = nullptr; // 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) { - swError("[Master] Fatal Error: failed to allocate memory for Server->gs"); + swoole_error("[Master] Fatal Error: failed to allocate memory for Server->gs"); } + gs->pipe_packet_msg_id = 1; + gs->max_concurrency = UINT_MAX; - worker_msg_id = 1; - worker_buffer_allocator = sw_std_allocator(); + 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; } @@ -666,29 +734,37 @@ Server::~Server() { } int Server::create() { - if (factory) { + if (is_created()) { return SW_ERR; } + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this); + } + session_list = (Session *) sw_shm_calloc(SW_SESSION_LIST_SIZE, sizeof(Session)); if (session_list == nullptr) { - swError("sw_shm_calloc(%ld) for session_list failed", SW_SESSION_LIST_SIZE * sizeof(Session)); + swoole_error("sw_shm_calloc(%ld) for session_list failed", SW_SESSION_LIST_SIZE * sizeof(Session)); return SW_ERR; } - port_connnection_num_list = (uint32_t *) sw_shm_calloc(ports.size(), sizeof(sw_atomic_t)); - if (port_connnection_num_list == nullptr) { - swError("sw_shm_calloc() for port_connnection_num_array failed"); + port_gs_list = (ServerPortGS *) sw_shm_calloc(ports.size(), sizeof(ServerPortGS)); + if (port_gs_list == nullptr) { + swoole_error("sw_shm_calloc() for port_connnection_num_array failed"); return SW_ERR; } int index = 0; for (auto port : ports) { - port->connection_num = &port_connnection_num_list[index++]; + port->gs = &port_gs_list[index++]; } 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 @@ -698,13 +774,13 @@ int Server::create() { } if (max_connection < minimum_connection) { max_connection = SwooleG.max_sockets; - swWarn("max_connection must be bigger than %u, it's reset to %u", minimum_connection, SwooleG.max_sockets); + swoole_warning( + "max_connection must be bigger than %u, it's reset to %u", minimum_connection, SwooleG.max_sockets); } // Reactor Thread Num if (reactor_num > SW_CPU_NUM * SW_MAX_THREAD_NCPU) { - swWarn("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; @@ -714,7 +790,7 @@ int Server::create() { } // Worker Process Num if (worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) { - swWarn( + swoole_warning( "worker_num == %d, Too many processes, reset to max value %d", worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; } @@ -724,25 +800,39 @@ int Server::create() { // TaskWorker Process Num if (task_worker_num > 0) { if (task_worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) { - swWarn("serv->task_worker_num == %d, Too many processes, reset to max value %d", - task_worker_num, - SW_CPU_NUM * SW_MAX_WORKER_NCPU); + 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; } } workers = (Worker *) sw_shm_calloc(worker_num, sizeof(Worker)); if (workers == nullptr) { - swSysWarn("gmalloc[server->workers] failed"); + swoole_sys_warning("gmalloc[server->workers] failed"); return SW_ERR; } if (is_base_mode()) { - factory = new BaseFactory(this); - return create_reactor_processes(); + factory = create_base_factory(); + } else if (is_thread_mode()) { + factory = create_thread_factory(); } else { - factory = new ProcessFactory(this); - return create_reactor_threads(); + factory = create_process_factory(); } + if (!factory) { + return SW_ERR; + } + + if (is_process_mode()) { + reactor_thread_barrier.init(false, reactor_num + 1); + gs->manager_barrier.init(true, 2); + } + + if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE)) { + swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); + } + + return SW_OK; } void Server::clear_timer() { @@ -760,68 +850,172 @@ void Server::clear_timer() { } } -void Server::shutdown() { - 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; } - 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; - } + + 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()) { + 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 (!port->socket->removed) { reactor->del(port->socket); } - clear_timer(); } + 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; + } + 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; +} - swInfo("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() { - swTraceLog(SW_TRACE_SERVER, "release service"); - if (SwooleG.hooks[SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN]) { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); + swoole_trace_log(SW_TRACE_SERVER, "release service"); + 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()) { - swTraceLog(SW_TRACE_SERVER, "terminate task workers"); + swoole_trace_log(SW_TRACE_SERVER, "terminate task workers"); if (task_worker_num > 0) { - gs->task_workers.shutdown(); gs->task_workers.destroy(); } - } else { - swTraceLog(SW_TRACE_SERVER, "terminate reactor threads"); + } else if (is_process_mode()) { + swoole_trace_log(SW_TRACE_SERVER, "terminate reactor threads"); /** * Wait until all the end of the thread */ join_reactor_thread(); } + release_pipe_buffers(); + for (auto port : ports) { port->close(); } - /** - * because the Worker in user_worker_list is the memory allocated by emalloc, - * the efree function will be called when the user process is destructed, - * so there's no need to call the efree here. - */ - if (user_worker_list) { - delete user_worker_list; - user_worker_list = nullptr; - } if (user_workers) { sw_shm_free(user_workers); user_workers = nullptr; @@ -830,7 +1024,7 @@ void Server::destroy() { ::close(null_fd); null_fd = -1; } - swSignal_clear(); + swoole_signal_clear(); /** * shutdown status */ @@ -842,31 +1036,31 @@ 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; } } + if (is_process_mode()) { + reactor_thread_barrier.destroy(); + gs->manager_barrier.destroy(); + } + 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_connnection_num_list); + sw_shm_free(port_gs_list); sw_shm_free(workers); session_list = nullptr; - port_connnection_num_list = nullptr; + port_gs_list = nullptr; workers = nullptr; delete factory; @@ -891,6 +1085,125 @@ bool Server::feedback(Connection *conn, enum ServerEventType event) { } } +bool Server::command(WorkerId process_id, + Command::ProcessType process_type, + const std::string &name, + const std::string &msg, + const Command::Callback &fn) { + if (!is_started()) { + return false; + } + auto iter = commands.find(name); + if (iter == commands.end()) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[%s]", name.c_str()); + return false; + } + + 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() && 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; + } + + if (is_base_mode() && process_type == Command::EVENT_WORKER && process_id == 0) { + process_type = Command::MASTER; + } + + if (is_process_mode() && process_type == Command::REACTOR_THREAD && process_id == reactor_num) { + process_type = Command::MASTER; + process_id = 0; + } + + int command_id = iter->second.id; + int64_t request_id = command_current_request_id++; + Socket *pipe_sock; + + SendData task{}; + 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]"); + goto _fail; + } + if (process_id >= reactor_num) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid thread_id[%d]", process_id); + 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); + 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); + goto _fail; + } + EventData buf; + if (!task_pack(&buf, msg.c_str(), msg.length())) { + goto _fail; + } + buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; + buf.info.fd = request_id; + buf.info.server_fd = command_id; + int _dst_worker_id = process_id; + if (!this->task(&buf, &_dst_worker_id)) { + goto _fail; + } + return true; + } else if (process_type == Command::MANAGER) { + EventData buf; + if (msg.length() >= sizeof(buf.data)) { + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_DATA_LENGTH_TOO_LARGE, + "message is too large, maximum length is %lu, the given length is %lu", + sizeof(buf.data), + msg.length()); + goto _fail; + } + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; + 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) { + goto _fail; + } + return true; + } else if (process_type == Command::MASTER) { + auto result = call_command_handler_in_master(command_id, msg); + fn(this, result); + return true; + } else { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [process_type]"); + goto _fail; + } + if (!message_bus.write(pipe_sock, &task)) { + goto _fail; + } + return true; +} + void Server::store_pipe_fd(UnixSocket *p) { Socket *master_socket = p->get_socket(true); Socket *worker_socket = p->get_socket(false); @@ -912,10 +1225,23 @@ void Server::store_pipe_fd(UnixSocket *p) { bool Server::send(SessionId session_id, const void *data, uint32_t length) { SendData _send{}; _send.info.fd = session_id; - _send.info.type = SW_SERVER_EVENT_RECV_DATA; + _send.info.type = SW_SERVER_EVENT_SEND_DATA; _send.data = (char *) data; _send.info.len = length; - return factory->finish(&_send); + if (factory->finish(&_send)) { + sw_atomic_fetch_add(&gs->response_count, 1); + sw_atomic_fetch_add(&gs->total_send_bytes, length); + ListenPort *port = get_port_by_session_id(session_id); + if (port) { + sw_atomic_fetch_add(&port->gs->response_count, 1); + sw_atomic_fetch_add(&port->gs->total_send_bytes, length); + } + if (sw_worker()) { + sw_worker()->response_count++; + } + return true; + } + return false; } int Server::schedule_worker(int fd, SendData *data) { @@ -923,21 +1249,21 @@ int Server::schedule_worker(int fd, SendData *data) { if (dispatch_func) { int id = dispatch_func(this, get_connection(fd), data); - if (id != SW_DISPATCH_RESULT_USERFUNC_FALLBACK) { + if (id != DISPATCH_RESULT_USERFUNC_FALLBACK) { return id; } } // polling mode - if (dispatch_mode == SW_DISPATCH_ROUND) { + if (dispatch_mode == DISPATCH_ROUND) { key = sw_atomic_fetch_add(&worker_round_id, 1); } // Using the FD touch access to hash - else if (dispatch_mode == SW_DISPATCH_FDMOD) { + else if (dispatch_mode == DISPATCH_FDMOD) { key = fd; } // Using the IP touch access to hash - else if (dispatch_mode == SW_DISPATCH_IPMOD) { + else if (dispatch_mode == DISPATCH_IPMOD) { Connection *conn = get_connection(fd); // UDP if (conn == nullptr) { @@ -957,14 +1283,14 @@ int Server::schedule_worker(int fd, SendData *data) { key = conn->info.addr.inet_v6.sin6_addr.s6_addr32[3]; #endif } - } else if (dispatch_mode == SW_DISPATCH_UIDMOD) { + } else if (dispatch_mode == DISPATCH_UIDMOD) { Connection *conn = get_connection(fd); if (conn == nullptr || conn->uid == 0) { key = fd; } else { key = conn->uid; } - } else if (dispatch_mode == SW_DISPATCH_CO_CONN_LB) { + } else if (dispatch_mode == DISPATCH_CO_CONN_LB) { Connection *conn = get_connection(fd); if (conn == nullptr) { return key % worker_num; @@ -973,25 +1299,14 @@ int Server::schedule_worker(int fd, SendData *data) { conn->worker_id = get_lowest_load_worker_id(); } return conn->worker_id; - } else if (dispatch_mode == SW_DISPATCH_CO_REQ_LB) { + } 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(); } - // Preemptive distribution + // 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; - } - swTraceLog(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); - return key; + return get_idle_worker_id(); } return key % worker_num; @@ -1013,14 +1328,14 @@ int Server::send_to_connection(SendData *_send) { conn = get_connection_verify_no_ssl(session_id); } if (!conn) { - if (_send->info.type == SW_SERVER_EVENT_RECV_DATA) { - swoole_error_log(SW_LOG_NOTICE, + if (_send->info.type == SW_SERVER_EVENT_SEND_DATA) { + 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, @@ -1038,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 { @@ -1123,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 @@ -1141,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 @@ -1150,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) { @@ -1175,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()) { @@ -1189,7 +1499,7 @@ int Server::send_to_connection(SendData *_send) { * use in master process */ bool Server::notify(Connection *conn, enum ServerEventType event) { - DataHead notify_event = {}; + DataHead notify_event{}; notify_event.type = event; notify_event.reactor_id = conn->reactor_id; notify_event.fd = conn->fd; @@ -1222,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) @@ -1234,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; @@ -1257,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; @@ -1267,26 +1584,8 @@ bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { return conn->socket->send_blocking(data, length) == length; } -size_t Server::get_packet(EventData *req, char **data_ptr) { - size_t length; - if (req->info.flags & SW_EVENT_DATA_PTR) { - PacketPtr *task = (PacketPtr *) req; - *data_ptr = task->data.str; - length = task->data.length; - } else if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { - String *worker_buffer; - memcpy(&worker_buffer, req->data, sizeof(worker_buffer)); - *data_ptr = worker_buffer->str; - length = worker_buffer->length; - } else { - *data_ptr = req->data; - length = req->info.len; - } - - return length; -} - void Server::call_hook(HookType type, void *arg) { + assert(type <= HOOK_END); swoole::hook_call(hooks, type, arg); } @@ -1294,27 +1593,35 @@ void Server::call_hook(HookType type, void *arg) { * [Worker] */ bool Server::close(SessionId session_id, bool reset) { - return factory->end(session_id, reset ? CLOSE_ACTIVELY | CLOSE_RESET : CLOSE_ACTIVELY); + 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() { - swSignal_set(SIGPIPE, nullptr); - swSignal_set(SIGHUP, nullptr); + swoole_signal_set(SIGPIPE, nullptr); + swoole_signal_set(SIGHUP, nullptr); if (is_process_mode()) { - swSignal_set(SIGCHLD, Server_signal_handler); + swoole_signal_set(SIGCHLD, master_signal_handler); } else { - swSignal_set(SIGIO, Server_signal_handler); + swoole_signal_set(SIGIO, master_signal_handler); } - swSignal_set(SIGUSR1, Server_signal_handler); - swSignal_set(SIGUSR2, Server_signal_handler); - swSignal_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 - swSignal_set(SIGRTMIN, Server_signal_handler); + swoole_signal_set(SIGRTMIN, master_signal_handler); #endif // for test - swSignal_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) { @@ -1335,19 +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); } -} -int Server::add_worker(Worker *worker) { - if (user_worker_list == nullptr) { - user_worker_list = new std::vector(); - } - user_worker_num++; - user_worker_list->push_back(worker); - - if (!user_worker_map) { - user_worker_map = new std::unordered_map(); + 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; } @@ -1355,6 +1659,32 @@ int Server::add_hook(Server::HookType type, const Callback &func, int push_back) return swoole::hook_add(hooks, (int) type, func, push_back); } +bool Server::add_command(const std::string &name, int accepted_process_types, const Command::Handler &func) { + if (is_started()) { + return false; + } + if (commands.find(name) != commands.end()) { + return false; + } + if (!is_base_mode() && pipe_command == nullptr) { + auto _pipe = new UnixSocket(false, SOCK_DGRAM); + if (!_pipe->ready()) { + delete _pipe; + return false; + } + pipe_command = _pipe; + } + int command_id = command_current_id++; + Command command{ + command_id, + accepted_process_types, + name, + }; + commands.emplace(name, command); + command_handlers[command_id] = func; + return true; +} + void Server::check_port_type(ListenPort *ls) { if (ls->is_dgram()) { // dgram socket, setting socket buffer size @@ -1396,7 +1726,7 @@ bool Server::is_healthy_connection(double now, Connection *conn) { int Server::add_systemd_socket() { int pid; if (!swoole_get_env("LISTEN_PID", &pid) && getpid() != pid) { - swWarn("invalid LISTEN_PID"); + swoole_warning("invalid LISTEN_PID"); return 0; } @@ -1412,7 +1742,7 @@ int Server::add_systemd_socket() { if (!swoole_get_env("LISTEN_FDS_START", &start_fd)) { start_fd = SW_SYSTEMD_FDS_START; } else if (start_fd < 0) { - swWarn("invalid LISTEN_FDS_START"); + swoole_warning("invalid LISTEN_FDS_START"); return 0; } @@ -1436,7 +1766,7 @@ int Server::add_systemd_socket() { return count; } -ListenPort *Server::add_port(enum swSocket_type type, const char *host, int port) { +ListenPort *Server::add_port(SocketType type, const char *host, int port) { if (session_list) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, "must add port before server is created"); return nullptr; @@ -1470,7 +1800,7 @@ ListenPort *Server::add_port(enum swSocket_type type, const char *host, int port #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (enum swSocket_type)(type & (~SW_SOCK_SSL)); + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); @@ -1485,101 +1815,54 @@ ListenPort *Server::add_port(enum swSocket_type 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 - swWarn("DTLS support require openssl-1.1 or later"); + swoole_warning("DTLS support require openssl-1.1 or later"); return nullptr; #endif } } #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) { - swTraceLog(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", sig, swSignal_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) { - swWarn("Fatal Error: manager process exit. status=%d, signal=[%s]", - WEXITSTATUS(status), - swSignal_str(WTERMSIG(status))); - } + serv->signal_handler_child_exit(); break; - /** - * for test - */ case SIGVTALRM: - swWarn("SIGVTALRM coming"); + swoole_warning("SIGVTALRM coming"); break; - /** - * proxy the restart signal - */ case SIGUSR1: case SIGUSR2: - if (serv->is_base_mode()) { - if (serv->gs->event_workers.reloading) { - break; - } - serv->gs->event_workers.reloading = true; - 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->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->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; @@ -1595,26 +1878,52 @@ void Server::foreach_connection(const std::function &callbac } } +void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) { + sw_atomic_fetch_add(&gs->abort_count, 1); + sw_atomic_fetch_add(&ls->gs->abort_count, 1); + if (_socket->object) { + reactor->close(reactor, _socket); + } else { + _socket->free(); + } +} + +// 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 */ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_fd) { - gs->accept_count++; - sw_atomic_fetch_add(&gs->connection_num, 1); - sw_atomic_fetch_add(ls->connection_num, 1); - int fd = _socket->fd; - lock(); - if (fd > get_maxfd()) { - set_maxfd(fd); - } else if (fd < get_minfd()) { - set_minfd(fd); - } - unlock(); - 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); @@ -1646,8 +1955,8 @@ 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) { - swSysWarn("setsockopt(TCP_NODELAY) failed"); + if (_socket->set_tcp_nodelay() != 0) { + swoole_sys_warning("setsockopt(TCP_NODELAY) failed"); } _socket->enable_tcp_nodelay = true; } @@ -1655,14 +1964,14 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f // socket recv buffer size if (ls->kernel_socket_recv_buffer_size > 0) { if (ls->socket->set_option(SOL_SOCKET, SO_RCVBUF, ls->kernel_socket_recv_buffer_size) != 0) { - swSysWarn("setsockopt(SO_RCVBUF, %d) failed", ls->kernel_socket_recv_buffer_size); + swoole_sys_warning("setsockopt(SO_RCVBUF, %d) failed", ls->kernel_socket_recv_buffer_size); } } // socket send buffer size if (ls->kernel_socket_send_buffer_size > 0) { if (ls->socket->set_option(SOL_SOCKET, SO_SNDBUF, ls->kernel_socket_send_buffer_size) != 0) { - swSysWarn("setsockopt(SO_SNDBUF, %d) failed", ls->kernel_socket_send_buffer_size); + swoole_sys_warning("setsockopt(SO_SNDBUF, %d) failed", ls->kernel_socket_send_buffer_size); } } @@ -1683,11 +1992,29 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f _socket->direct_send = 1; } + lock(); + if (fd > get_maxfd()) { + set_maxfd(fd); + } else if (fd < get_minfd()) { + set_minfd(fd); + } + unlock(); + + gs->accept_count++; + ls->gs->accept_count++; + 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::set_ipc_max_size() { -#ifdef HAVE_KQUEUE +void Server::init_ipc_max_size() { +#ifndef __linux__ ipc_max_size = SW_IPC_MAX_SIZE; #else int bufsize; @@ -1697,29 +2024,34 @@ void Server::set_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 */ int Server::create_pipe_buffers() { - pipe_buffers = (PipeBuffer **) sw_calloc(reactor_num, sizeof(PipeBuffer *)); - if (pipe_buffers == nullptr) { - swSysError("malloc[buffers] failed"); - return SW_ERR; - } - for (uint32_t i = 0; i < reactor_num; i++) { - pipe_buffers[i] = (PipeBuffer *) sw_malloc(ipc_max_size); - if (pipe_buffers[i] == nullptr) { - swSysError("malloc[sndbuf][%d] failed", i); - return SW_ERR; - } - sw_memset_zero(pipe_buffers[i], sizeof(DataHead)); - } + message_bus.set_buffer_size(ipc_max_size); + return message_bus.alloc_buffer() ? SW_OK : SW_ERR; +} - return SW_OK; +void Server::release_pipe_buffers() { + message_bus.free_buffer(); } int Server::get_idle_worker_num() { @@ -1728,7 +2060,7 @@ int Server::get_idle_worker_num() { 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++; } } @@ -1737,16 +2069,25 @@ int Server::get_idle_worker_num() { } int Server::get_idle_task_worker_num() { - uint32_t i; uint32_t idle_worker_num = 0; - for (i = worker_num; i < (worker_num + task_worker_num); i++) { + 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++; } } return idle_worker_num; } + +int Server::get_tasking_num() { + // TODO Why need to reset ? + int tasking_num = gs->tasking_num; + if (tasking_num < 0) { + tasking_num = gs->tasking_num = 0; + } + return tasking_num; +} + } // namespace swoole diff --git a/src/server/port.cc b/src/server/port.cc index f4bb2682e44..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; @@ -93,7 +92,7 @@ static int ssl_server_sni_callback(SSL *ssl, int *al, void *arg) { return SSL_TLSEXT_ERR_NOACK; } - ListenPort *port = (ListenPort *) SSL_get_ex_data(ssl, swSSL_get_ex_port_index()); + ListenPort *port = (ListenPort *) SSL_get_ex_data(ssl, swoole_ssl_get_ex_port_index()); if (port->sni_contexts.empty()) { return SSL_TLSEXT_ERR_NOACK; @@ -124,8 +123,8 @@ bool ListenPort::ssl_create(Connection *conn, Socket *sock) { return false; } conn->ssl = 1; - if (SSL_set_ex_data(sock->ssl, swSSL_get_ex_port_index(), this) == 0) { - swWarn("SSL_set_ex_data() failed"); + if (SSL_set_ex_data(sock->ssl, swoole_ssl_get_ex_port_index(), this) == 0) { + swoole_warning("SSL_set_ex_data() failed"); return false; } return true; @@ -133,7 +132,7 @@ bool ListenPort::ssl_create(Connection *conn, Socket *sock) { bool ListenPort::ssl_create_context(SSLContext *context) { if (context->cert_file.empty() || context->key_file.empty()) { - swWarn("SSL error, require ssl_cert_file and ssl_key_file"); + swoole_warning("SSL error, require ssl_cert_file and ssl_key_file"); return false; } if (open_http_protocol) { @@ -143,7 +142,7 @@ bool ListenPort::ssl_create_context(SSLContext *context) { context->http_v2 = 1; } if (!context->create()) { - swWarn("swSSL_get_context() error"); + swoole_warning("failed to create ssl content"); return false; } return true; @@ -153,7 +152,7 @@ bool ListenPort::ssl_create_context(SSLContext *context) { int ListenPort::listen() { // listen stream socket if (!listening && socket->listen(backlog) < 0) { - swSysWarn("listen(%s:%d, %d) failed", host.c_str(), port, backlog); + swoole_sys_warning("listen(%s:%d, %d) failed", host.c_str(), port, backlog); return SW_ERR; } listening = true; @@ -161,7 +160,7 @@ int ListenPort::listen() { #ifdef TCP_DEFER_ACCEPT if (tcp_defer_accept) { if (socket->set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, tcp_defer_accept) != 0) { - swSysWarn("setsockopt(TCP_DEFER_ACCEPT) failed"); + swoole_sys_warning("setsockopt(TCP_DEFER_ACCEPT) failed"); } } #endif @@ -172,7 +171,7 @@ int ListenPort::listen() { memset(&a, 0, sizeof(a)); strcpy(a.af_name, "httpready"); if (socket->set_option(SOL_SOCKET, SO_ACCEPTFILTER, &a, sizeof(a)) != 0) { - swSysWarn("setsockopt(SO_ACCEPTFILTER) failed"); + swoole_sys_warning("setsockopt(SO_ACCEPTFILTER) failed"); } } #endif @@ -180,7 +179,7 @@ int ListenPort::listen() { #ifdef TCP_FASTOPEN if (tcp_fastopen) { if (socket->set_option(IPPROTO_TCP, TCP_FASTOPEN, tcp_fastopen) != 0) { - swSysWarn("setsockopt(TCP_FASTOPEN) failed"); + swoole_sys_warning("setsockopt(TCP_FASTOPEN) failed"); } } #endif @@ -188,22 +187,22 @@ int ListenPort::listen() { #ifdef SO_KEEPALIVE if (open_tcp_keepalive == 1) { if (socket->set_option(SOL_SOCKET, SO_KEEPALIVE, 1) != 0) { - swSysWarn("setsockopt(SO_KEEPALIVE) failed"); + swoole_sys_warning("setsockopt(SO_KEEPALIVE) failed"); } #ifdef TCP_KEEPIDLE if (socket->set_option(IPPROTO_TCP, TCP_KEEPIDLE, tcp_keepidle) < 0) { - swSysWarn("setsockopt(TCP_KEEPIDLE) failed"); + swoole_sys_warning("setsockopt(TCP_KEEPIDLE) failed"); } if (socket->set_option(IPPROTO_TCP, TCP_KEEPINTVL, tcp_keepinterval) < 0) { - swSysWarn("setsockopt(TCP_KEEPINTVL) failed"); + swoole_sys_warning("setsockopt(TCP_KEEPINTVL) failed"); } if (socket->set_option(IPPROTO_TCP, TCP_KEEPCNT, tcp_keepcount) < 0) { - swSysWarn("setsockopt(TCP_KEEPCNT) failed"); + swoole_sys_warning("setsockopt(TCP_KEEPCNT) failed"); } #endif #ifdef TCP_USER_TIMEOUT if (tcp_user_timeout > 0 && socket->set_option(IPPROTO_TCP, TCP_USER_TIMEOUT, tcp_user_timeout) != 0) { - swSysWarn("setsockopt(TCP_USER_TIMEOUT) failed"); + swoole_sys_warning("setsockopt(TCP_USER_TIMEOUT) failed"); } #endif } @@ -232,19 +231,16 @@ 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 = swHttpMix_get_package_length; - ls->protocol.get_package_length_size = swHttpMix_get_package_length_size; - ls->protocol.onPackage = swHttpMix_dispatch_frame; + ls->protocol.get_package_length = http_server::get_package_length; + ls->protocol.get_package_length_size = http_server::get_package_length_size; + ls->protocol.onPackage = http_server::dispatch_frame; } else if (ls->open_http2_protocol) { 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; } @@ -252,7 +248,7 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.package_body_offset = 0; ls->onRead = Port_onRead_http; } else if (ls->open_mqtt_protocol) { - swMqtt_set_protocol(&ls->protocol); + mqtt::set_protocol(&ls->protocol); ls->protocol.onPackage = Server::dispatch_task; ls->onRead = Port_onRead_check_length; } else if (ls->open_redis_protocol) { @@ -274,11 +270,11 @@ bool ListenPort::import(int sock) { // get socket type if (socket->get_option(SOL_SOCKET, SO_TYPE, &_type) < 0) { - swSysWarn("getsockopt(%d, SOL_SOCKET, SO_TYPE) failed", sock); + swoole_sys_warning("getsockopt(%d, SOL_SOCKET, SO_TYPE) failed", sock); return false; } if (socket->get_name(&socket->info) < 0) { - swSysWarn("getsockname(%d) failed", sock); + swoole_sys_warning("getsockname(%d) failed", sock); return false; } @@ -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,9 +314,9 @@ 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: - swSysWarn("recv from connection#%d failed", event->fd); + swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; case SW_CLOSE: conn->close_errno = errno; @@ -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); } } @@ -352,7 +349,7 @@ static int Port_onRead_check_length(Reactor *reactor, ListenPort *port, Event *e } if (protocol->recv_with_length_protocol(_socket, buffer) < 0) { - swTrace("Close Event.FD=%d|From=%d", event->fd, event->reactor_id); + swoole_trace("Close Event.FD=%d|From=%d", event->fd, event->reactor_id); conn->close_errno = errno; reactor->trigger_close_event(event); } @@ -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,9 +414,9 @@ 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: - swSysWarn("recv from connection#%d failed", event->fd); + swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; case SW_CLOSE: conn->close_errno = errno; @@ -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(); - swTraceLog(SW_TRACE_SERVER, - "content-length=%u, keep-alive=%u, chunked=%u", - request->content_length_, - request->keep_alive, - request->chunked); + request->max_length_ = protocol->package_max_length; + swoole_trace_log(SW_TRACE_SERVER, + "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_; } - swTraceLog(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_, @@ -614,11 +638,12 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (request->has_expect_header()) { _socket->send(SW_STRL(SW_HTTP_100_CONTINUE_PACKET), 0); } else { - swTraceLog(SW_TRACE_SERVER, - "PostWait: request->content_length=%d, buffer->length=%zu, request->header_length=%d\n", - request->content_length, - buffer_->length, - request->header_length); + swoole_trace_log( + SW_TRACE_SERVER, + "PostWait: request->content_length=%d, buffer->length=%zu, request->header_length=%d\n", + request->content_length, + buffer_->length, + request->header_length); } #endif goto _recv_data; @@ -636,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); @@ -663,7 +693,7 @@ static int Port_onRead_redis(Reactor *reactor, ListenPort *port, Event *event) { return SW_ERR; } - if (swRedis_recv_packet(protocol, conn, buffer) < 0) { + if (redis::recv_packet(protocol, conn, buffer) < 0) { conn->close_errno = errno; reactor->trigger_close_event(event); } @@ -723,4 +753,95 @@ void ListenPort::close() { } } +const char *ListenPort::get_protocols() { + if (is_dgram()) { + return "dgram"; + } + if (open_eof_check) { + return "eof"; + } else if (open_length_check) { + return "length"; + } else if (open_http_protocol) { + if (open_http2_protocol && open_websocket_protocol) { + return "http|http2|websocket"; + } else if (open_http2_protocol) { + return "http|http2"; + } else if (open_websocket_protocol) { + return "http|websocket"; + } else { + return "http"; + } + } else if (open_mqtt_protocol) { + return "mqtt"; + } else if (open_redis_protocol) { + return "redis"; + } else { + return "raw"; + } +} + +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 1a02540d2cf..d2e028f60dd 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -10,121 +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; -typedef int (*SendFunc)(Server *serv, DataHead *head, const iovec *iov, size_t iovcnt, void *private_data); +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); +} -static bool process_send_packet(Server *serv, SendData *resp, SendFunc _send, void *private_data); -static int process_sendto_worker(Server *serv, DataHead *head, const iovec *iov, size_t iovcnt, void *private_data); -static int process_sendto_reactor(Server *serv, DataHead *head, const iovec *iov, size_t iovcnt, void *private_data); +void Server::destroy_process_factory() { + sw_shm_free(connection_list); + delete[] reactor_threads; -ProcessFactory::ProcessFactory(Server *server) : Factory(server) { - send_buffer = nullptr; + if (gs->event_workers.message_box) { + gs->event_workers.message_box->destroy(); + } } -bool ProcessFactory::shutdown() { - int status; +ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} - if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) { - swSysWarn("swKill(%d) failed", server_->gs->manager_pid); +ProcessFactory::~ProcessFactory() {} + +/** + * 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) { - swSysWarn("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_->pipe_buffers) { - SW_LOOP_N(server_->reactor_num) { - sw_free(server_->pipe_buffers[i]); - } - sw_free(server_->pipe_buffers); +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_->stream_socket_file) { - unlink(server_->stream_socket_file); - sw_free(server_->stream_socket_file); - server_->stream_socket->free(); + + 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; +} + +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 (send_buffer) { - sw_free(send_buffer); + 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; } } -bool ProcessFactory::start() { - if (server_->dispatch_mode == SW_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_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) { - server_->create_worker(server_->get_worker(i)); +bool ProcessFactory::shutdown() { + int status; + + if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d) failed", server_->gs->manager_pid); } - SW_LOOP_N(server_->worker_num) { - int kernel_buffer_size = SW_UNIXSOCK_MAX_BUF_SIZE; + 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); - } - - server_->set_ipc_max_size(); - if (server_->create_pipe_buffers() < 0) { - return false; + 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; } - send_buffer = (PipeBuffer *) sw_malloc(server_->ipc_max_size); - if (send_buffer == nullptr) { - swSysError("malloc[send_buffer] failed"); + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { return false; } - sw_memset_zero(send_buffer, sizeof(DataHead)); + return true; +} - /** - * The manager process must be started first, otherwise it will have a thread fork - */ - if (server_->start_manager_process() < 0) { - swWarn("FactoryProcess_manager_start failed"); +bool ProcessFactory::start() { + if (!server_->create_worker_pipes()) { return false; } - return true; + return server_->start_manager_process() == SW_OK; } /** @@ -137,16 +219,6 @@ bool ProcessFactory::notify(DataHead *ev) { return dispatch(&task); } -static inline int process_sendto_worker( - Server *serv, DataHead *head, const iovec *iov, size_t iovcnt, void *private_data) { - return serv->send_to_worker_from_master((Worker *) private_data, iov, iovcnt); -} - -static inline int process_sendto_reactor( - Server *serv, DataHead *head, const iovec *iov, size_t iovcnt, void *private_data) { - return serv->send_to_reactor_thread(head, iov, iovcnt, ((Connection *) private_data)->session_id); -} - /** * [ReactorThread] dispatch request to worker */ @@ -156,13 +228,13 @@ bool ProcessFactory::dispatch(SendData *task) { int target_worker_id = server_->schedule_worker(fd, task); if (target_worker_id < 0) { switch (target_worker_id) { - case SW_DISPATCH_RESULT_DISCARD_PACKET: + case Server::DISPATCH_RESULT_DISCARD_PACKET: return false; - case SW_DISPATCH_RESULT_CLOSE_CONNECTION: + case Server::DISPATCH_RESULT_CLOSE_CONNECTION: // TODO: close connection return false; default: - swWarn("invalid target worker id[%d]", target_worker_id); + swoole_warning("invalid target worker id[%d]", target_worker_id); return false; } } @@ -170,7 +242,7 @@ bool ProcessFactory::dispatch(SendData *task) { if (Server::is_stream_event(task->info.type)) { Connection *conn = server_->get_connection(fd); if (conn == nullptr || conn->active == 0) { - swWarn("dispatch[type=%d] failed, connection#%d is not active", task->info.type, fd); + swoole_warning("dispatch[type=%d] failed, connection#%d is not active", task->info.type, fd); return false; } // server active close, discard data. @@ -188,105 +260,30 @@ bool ProcessFactory::dispatch(SendData *task) { Worker *worker = server_->get_worker(target_worker_id); if (task->info.type == SW_SERVER_EVENT_RECV_DATA) { - worker->dispatch_count++; - server_->gs->dispatch_count++; + sw_atomic_fetch_add(&worker->dispatch_count, 1); } SendData _task; memcpy(&_task, task, sizeof(SendData)); + network::Socket *sock; + MessageBus *mb; - return process_send_packet(server_, &_task, process_sendto_worker, worker); -} - -/** - * @description: master process send data to worker process. - * 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 SW_OK, send failure returns SW_ERR. - */ -static bool process_send_packet(Server *serv, SendData *resp, SendFunc _send, void *private_data) { - const char *data = resp->data; - uint32_t l_payload = resp->info.len; - off_t offset = 0; - uint32_t copy_n; - - struct iovec iov[2]; - - uint32_t max_length = serv->ipc_max_size - sizeof(resp->info); - resp->info.msg_id = serv->worker_msg_id.fetch_add(1); - - if (l_payload <= max_length) { - resp->info.flags = 0; - resp->info.len = l_payload; - - size_t iovcnt; - iov[0].iov_base = &resp->info; - iov[0].iov_len = sizeof(resp->info); - - if (resp->data && l_payload > 0) { - iov[1].iov_base = (void *) resp->data; - iov[1].iov_len = l_payload; - iovcnt = 2; - } else { - iovcnt = 1; - } - - int retval = _send(serv, &resp->info, iov, iovcnt, private_data); -#ifdef __linux__ - if (retval < 0 && errno == ENOBUFS) { - max_length = SW_IPC_BUFFER_SIZE; - goto _ipc_use_chunk; - } -#endif - return retval >= 0; - } - -#ifdef __linux__ -_ipc_use_chunk: -#endif - resp->info.flags = SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN; - resp->info.len = l_payload; - - while (l_payload > 0) { - if (l_payload > max_length) { - copy_n = max_length; - } else { - resp->info.flags |= SW_EVENT_DATA_END; - copy_n = l_payload; - } - - iov[0].iov_base = &resp->info; - iov[0].iov_len = sizeof(resp->info); - iov[1].iov_base = (void *) (data + offset); - iov[1].iov_len = copy_n; - - swTrace("finish, type=%d|len=%u", resp->info.type, copy_n); - - if (_send(serv, &resp->info, iov, 2, private_data) < 0) { -#ifdef __linux__ - if (errno == ENOBUFS && max_length > SW_BUFFER_SIZE_STD) { - max_length = SW_IPC_BUFFER_SIZE; - continue; - } -#endif - return false; - } - - if (resp->info.flags & SW_EVENT_DATA_BEGIN) { - resp->info.flags &= ~SW_EVENT_DATA_BEGIN; - } - - l_payload -= copy_n; - offset += copy_n; + if (server_->is_reactor_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 true; + 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(); } } @@ -315,16 +312,19 @@ 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, session_id); return false; - } else if (conn->overflow) { + } else if (conn->overflow && + (resp->info.type == SW_SERVER_EVENT_SEND_DATA || resp->info.type == SW_SERVER_EVENT_SEND_FILE)) { if (server_->send_yield && process_is_supported_send_yield(server_, conn)) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { @@ -336,33 +336,15 @@ 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(); - swTrace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); + swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); - return process_send_packet(server_, &task, process_sendto_reactor, conn); + return server_->message_bus.write(server_->get_reactor_pipe_socket(session_id, task.info.reactor_id), &task); } bool ProcessFactory::end(SessionId session_id, int flags) { @@ -375,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. @@ -387,65 +369,52 @@ bool ProcessFactory::end(SessionId session_id, int flags) { conn->close_actively = 1; } - swTraceLog(SW_TRACE_CLOSE, "session_id=%ld, fd=%d", session_id, conn->fd); + swoole_trace_log(SW_TRACE_CLOSE, "session_id=%ld, fd=%d", session_id, conn->fd); Worker *worker; 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: - if (conn == nullptr || conn->active == 0) { - swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); - return false; - } else if (conn->close_force) { - goto _do_close; - } else if (conn->closing) { + if (conn->closing) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; - } else if (conn->closed) { + } else if (!(conn->close_force || conn->close_reset) && conn->closed) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; - } else { - _do_close: - conn->closing = 1; - if (server_->onClose != nullptr) { - info.fd = session_id; - if (conn->close_actively) { - info.reactor_id = -1; - } else { - info.reactor_id = conn->reactor_id; - } - info.server_fd = conn->server_fd; - server_->onClose(server_, &info); + } + + if (server_->onClose != nullptr && !conn->closed) { + info.fd = session_id; + if (conn->close_actively) { + info.reactor_id = -1; + } else { + info.reactor_id = conn->reactor_id; } + info.server_fd = conn->server_fd; + conn->closing = 1; + server_->onClose(server_, &info); conn->closing = 0; - conn->closed = 1; - conn->close_errno = 0; - return finish(&_send); } + conn->closed = 1; + conn->close_errno = 0; + return finish(&_send); } } // namespace swoole diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index af855d12ff6..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 == nullptr; -} - -int Server::create_reactor_processes() { - reactor_num = worker_num; - connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swSysWarn("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) { - swSysWarn("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,10 @@ 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; SW_LOOP_N(worker_num) { gs->event_workers.workers[i].pool = &gs->event_workers; @@ -98,187 +71,95 @@ int Server::start_reactor_processes() { gs->event_workers.workers[i].type = SW_PROCESS_WORKER; } - // single worker - if (Server_is_single(this)) { - int retval = ReactorProcess_loop(&gs->event_workers, &gs->event_workers.workers[0]); + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { + return SW_ERR; + } + + 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) { - user_workers = (Worker *) sw_shm_calloc(user_worker_num, sizeof(Worker)); - if (user_workers == nullptr) { - swSysWarn("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.start(); - - init_signal_handler(); - - if (onStart) { - swWarn("The onStart event with SWOOLE_BASE is deprecated"); - onStart(this); - } - - 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) { - EventData task; SendData _send; Server *serv = (Server *) reactor->ptr; Factory *factory = serv->factory; - String *output_buffer; + PipeBuffer *pipe_buffer = serv->message_bus.get_buffer(); - ssize_t retval = read(event->fd, &task, sizeof(task)); + ssize_t retval = serv->message_bus.read(event->socket); if (retval <= 0) { - return SW_ERR; - } else if ((size_t) retval != task.info.len + sizeof(_send.info)) { - swWarn("bad pipeline data"); return SW_OK; } - switch (task.info.type) { - case SW_SERVER_EVENT_PIPE_MESSAGE: - serv->onPipeMessage(serv, &task); + switch (pipe_buffer->info.type) { + case SW_SERVER_EVENT_PIPE_MESSAGE: { + serv->onPipeMessage(serv, (EventData *) pipe_buffer); break; - case SW_SERVER_EVENT_FINISH: - serv->onFinish(serv, &task); + } + case SW_SERVER_EVENT_FINISH: { + serv->onFinish(serv, (EventData *) pipe_buffer); break; - case SW_SERVER_EVENT_SEND_FILE: - _send.info = task.info; - _send.data = task.data; + } + case SW_SERVER_EVENT_SEND_FILE: { + _send.info = pipe_buffer->info; + _send.data = pipe_buffer->data; factory->finish(&_send); break; - case SW_SERVER_EVENT_PROXY_START: - case SW_SERVER_EVENT_PROXY_END: - if (task.info.reactor_id < 0 || task.info.reactor_id >= (int16_t) serv->get_all_worker_num()) { - swWarn("invalid worker_id=%d", task.info.reactor_id); + } + case SW_SERVER_EVENT_SEND_DATA: { + if (pipe_buffer->info.reactor_id < 0 || pipe_buffer->info.reactor_id >= (int16_t) serv->get_all_worker_num()) { + swoole_warning("invalid worker_id=%d", pipe_buffer->info.reactor_id); return SW_OK; } - output_buffer = SwooleWG.output_buffer[task.info.reactor_id]; - output_buffer->append(task.data, task.info.len); - if (task.info.type == SW_SERVER_EVENT_PROXY_END) { - memcpy(&_send.info, &task.info, sizeof(_send.info)); - _send.info.type = SW_SERVER_EVENT_RECV_DATA; - _send.data = output_buffer->str; - _send.info.len = output_buffer->length; - factory->finish(&_send); - output_buffer->clear(); - } + auto packet = serv->message_bus.get_packet(); + memcpy(&_send.info, &pipe_buffer->info, sizeof(_send.info)); + _send.info.type = SW_SERVER_EVENT_RECV_DATA; + _send.data = packet.data; + _send.info.len = packet.length; + factory->finish(&_send); + break; + } + case SW_SERVER_EVENT_CLOSE: { + factory->end(pipe_buffer->info.fd, Server::CLOSE_ACTIVELY); break; - case SW_SERVER_EVENT_CLOSE: - factory->end(task.info.fd, Server::CLOSE_ACTIVELY); + } + case SW_SERVER_EVENT_COMMAND_REQUEST: { + serv->call_command_handler(serv->message_bus, sw_worker()->id, serv->get_worker(0)->pipe_master); + break; + } + case SW_SERVER_EVENT_COMMAND_RESPONSE: { + int64_t request_id = pipe_buffer->info.fd; + auto packet = serv->message_bus.get_packet(); + serv->call_command_callback(request_id, std::string(packet.data, packet.length)); break; + } default: break; } - return SW_OK; -} -static int ReactorProcess_alloc_output_buffer(size_t n_buffer) { - SwooleWG.output_buffer = (String **) sw_malloc(sizeof(String *) * n_buffer); - if (SwooleWG.output_buffer == nullptr) { - swError("malloc for SwooleWG.output_buffer failed"); - return SW_ERR; - } + serv->message_bus.pop(); - SW_LOOP_N(n_buffer) { - SwooleWG.output_buffer[i] = new String(SW_BUFFER_SIZE_BIG); - if (SwooleWG.output_buffer[i] == nullptr) { - swError("output_buffer init failed"); - return SW_ERR; - } - } return SW_OK; } -static void ReactorProcess_free_output_buffer(size_t n_buffer) { - SW_LOOP_N(n_buffer) { - delete SwooleWG.output_buffer[i]; - } - sw_free(SwooleWG.output_buffer); -} - -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; - if (worker->id == 0) { - SwooleTG.update_time = 1; - } + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); - serv->init_worker(worker); + serv->init_event_worker(worker); - // create reactor if (!SwooleTG.reactor) { if (swoole_event_init(0) < 0) { return SW_ERR; @@ -291,21 +172,24 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } - size_t n_buffer = serv->get_all_worker_num(); - if (ReactorProcess_alloc_output_buffer(n_buffer)) { - return SW_ERR; - } + 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) { - ReactorProcess_free_output_buffer(n_buffer); + 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; } @@ -313,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) { - swSignalfd_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 @@ -347,8 +224,8 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { // task workers if (serv->task_worker_num > 0) { - if (serv->task_ipc_mode == SW_TASK_IPC_UNIXSOCK) { - for (uint32_t i = 0; i < serv->gs->task_workers.worker_num; i++) { + if (serv->task_ipc_mode == Server::TASK_IPC_UNIXSOCK) { + SW_LOOP_N(serv->gs->task_workers.worker_num) { serv->gs->task_workers.workers[i].pipe_master->set_nonblock(); } } @@ -356,24 +233,21 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { serv->init_reactor(reactor); - // single server trigger onStart event - if (Server_is_single(serv)) { - if (serv->onStart) { + if (worker->id == 0) { + 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: - ReactorProcess_free_output_buffer(n_buffer); swoole_event_free(); return SW_ERR; } - serv->worker_start_callback(); + serv->worker_start_callback(worker); /** * for heartbeat check @@ -396,16 +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(); - ReactorProcess_free_output_buffer(n_buffer); + serv->worker_stop_callback(worker); return retval; } @@ -424,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 { @@ -460,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 4c3d8c3b878..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,26 +26,22 @@ using std::unordered_map; namespace swoole { using namespace network; -static void ReactorThread_loop(Server *serv, int reactor_id); -static int ReactorThread_init(Server *serv, Reactor *reactor, uint16_t 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 -static inline enum swReturn_code ReactorThread_verify_ssl_state(Reactor *reactor, ListenPort *port, Socket *_socket) { +static inline ReturnCode ReactorThread_verify_ssl_state(Reactor *reactor, ListenPort *port, Socket *_socket) { Server *serv = (Server *) reactor->ptr; if (!_socket->ssl || _socket->ssl_state == SW_SSL_STATE_READY) { return SW_CONTINUE; } - enum swReturn_code code = _socket->ssl_accept(); + ReturnCode code = _socket->ssl_accept(); if (code != SW_READY) { return code; } @@ -88,38 +83,12 @@ static inline enum swReturn_code ReactorThread_verify_ssl_state(Reactor *reactor } #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); @@ -141,7 +110,7 @@ static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) { if (errno == EAGAIN) { return SW_OK; } else { - swSysWarn("recvfrom(%d) failed", fd); + swoole_sys_warning("recvfrom(%d) failed", fd); return SW_ERR; } } @@ -172,7 +141,7 @@ static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) { ev.type = SW_SERVER_EVENT_INCOMING; ev.fd = conn->session_id; ev.reactor_id = conn->reactor_id; - if (serv->send_to_reactor_thread((EventData*) &ev, sizeof(ev), conn->session_id) < 0) { + if (serv->send_to_reactor_thread((EventData *) &ev, sizeof(ev), conn->session_id) < 0) { reactor->close(reactor, session->socket); return SW_OK; } @@ -216,9 +185,17 @@ 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); + + 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); + } - swTrace("Close Event.fd=%d|from=%d", socket->fd, reactor->id); + swoole_trace("Close Event.fd=%d|from=%d", socket->fd, reactor->id); #ifdef SW_USE_OPENSSL if (socket->ssl) { @@ -240,8 +217,6 @@ int Server::close_connection(Reactor *reactor, Socket *socket) { socket->recv_buffer = nullptr; } - sw_atomic_fetch_sub(port->connection_num, 1); - if (port->open_http_protocol && conn->object) { serv->destroy_http_request(conn); } @@ -256,7 +231,7 @@ int Server::close_connection(Reactor *reactor, Socket *socket) { linger.l_onoff = 1; linger.l_linger = 0; if (conn->socket->set_option(SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)) != 0) { - swSysWarn("setsockopt(SO_LINGER) failed"); + swoole_sys_warning("setsockopt(SO_LINGER) failed"); } } #endif @@ -271,7 +246,7 @@ int Server::close_connection(Reactor *reactor, Socket *socket) { serv->lock(); if (fd == serv->get_maxfd()) { int find_max_fd = fd - 1; - swTrace("set_maxfd=%d|close_fd=%d\n", find_max_fd, fd); + swoole_trace("set_maxfd=%d|close_fd=%d\n", find_max_fd, fd); // find the new max_fd for (; !serv->is_valid_connection(serv->get_connection(find_max_fd)) && find_max_fd > serv->get_minfd(); find_max_fd--) { @@ -301,7 +276,7 @@ static int ReactorThread_onClose(Reactor *reactor, Event *event) { notify_ev.fd = fd; notify_ev.type = SW_SERVER_EVENT_CLOSE; - swTraceLog(SW_TRACE_CLOSE, "client[fd=%d] close the connection", fd); + swoole_trace_log(SW_TRACE_CLOSE, "client[fd=%d] close the connection", fd); Connection *conn = serv->get_connection(fd); if (conn == nullptr || conn->active == 0) { @@ -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,122 +339,91 @@ 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 */ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { SendData _send; - Server *serv = (Server *) reactor->ptr; ReactorThread *thread = serv->get_thread(reactor->id); - String *package = nullptr; - PipeBuffer *resp = serv->pipe_buffers[reactor->id]; -#ifdef SW_REACTOR_RECV_AGAIN - while (1) -#endif - { - ssize_t n = ev->socket->read(resp, serv->ipc_max_size); - if (n > 0) { - // packet chunk - if (resp->info.flags & SW_EVENT_DATA_CHUNK) { - int worker_id = resp->info.server_fd; - int key = (ev->fd << 16) + worker_id; - auto it = thread->send_buffers.find(key); - if (it == thread->send_buffers.end()) { - package = new String(SW_BUFFER_SIZE_BIG); - thread->send_buffers.emplace(std::make_pair(key, package)); - } else { - package = it->second; - } - // merge data to package buffer - package->append(resp->data, n - sizeof(resp->info)); - // wait more data - if (!(resp->info.flags & SW_EVENT_DATA_END)) { - return SW_OK; - } - _send.info = resp->info; - _send.data = package->str; - _send.info.len = package->length; - serv->send_to_connection(&_send); - delete package; - thread->send_buffers.erase(key); - } else { - /** - * 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); - } - } - /** - * server shutdown - */ - else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - ReactorThread_shutdown(reactor); - } 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); - } else { - _send.info = resp->info; - _send.data = resp->data; - serv->send_to_connection(&_send); - } + 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; + } + if (resp->info.type == SW_SERVER_EVENT_INCOMING) { + 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 (errno == EAGAIN) { - return SW_OK; + } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { + 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)); + } 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) { + thread->close_connection(reactor, resp->info.fd); } else { - swSysWarn("read(worker_pipe) failed"); - return SW_ERR; + PacketPtr packet = thread->message_bus.get_packet(); + _send.info = resp->info; + _send.info.len = packet.length; + _send.data = packet.data; + serv->send_to_connection(&_send); } + thread->message_bus.pop(); } return SW_OK; } -ssize_t Server::send_to_worker_from_master(Worker *worker, const iovec *iov, size_t iovcnt) { - if (SwooleTG.reactor) { - ReactorThread *thread = get_thread(SwooleTG.id); - Socket *socket = &thread->pipe_sockets[worker->pipe_master->fd]; - return swoole_event_writev(socket, iov, iovcnt); - } else { - return worker->pipe_master->writev_blocking(iov, iovcnt); - } -} - /** * [ReactorThread] worker pipe can write. */ @@ -476,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)) { @@ -503,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(); } @@ -513,7 +472,7 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { if (Buffer::empty(buffer)) { if (reactor->remove_write_event(ev->socket) < 0) { - swSysWarn("reactor->set(%d) failed", ev->fd); + swoole_sys_warning("reactor->set(%d) failed", ev->fd); } } @@ -532,10 +491,6 @@ void Server::init_reactor(Reactor *reactor) { // Read reactor->set_handler(SW_FD_SESSION | SW_EVENT_READ, ReactorThread_onRead); - if (dispatch_mode == SW_DISPATCH_STREAM) { - Client::init_reactor(reactor); - } - // listen the all tcp port for (auto port : ports) { if (port->is_dgram() @@ -568,15 +523,16 @@ static int ReactorThread_onRead(Reactor *reactor, Event *event) { dtls::Session *session = port->dtls_sessions->find(event->fd)->second; session->append(buffer); if (!session->listened && !session->listen()) { - Server::close_connection(reactor, event->socket); + serv->abort_connection(reactor, port, event->socket); return SW_OK; } } #endif - enum swReturn_code code = ReactorThread_verify_ssl_state(reactor, port, event->socket); + ReturnCode code = ReactorThread_verify_ssl_state(reactor, port, event->socket); switch (code) { case SW_ERROR: - return Server::close_connection(reactor, event->socket); + serv->abort_connection(reactor, port, event->socket); + return SW_OK; case SW_READY: #ifdef SW_SUPPORT_DTLS if (event->socket->dtls) { @@ -594,14 +550,22 @@ static int ReactorThread_onRead(Reactor *reactor, Event *event) { #endif conn->last_recv_time = microtime(); + long last_recv_bytes = event->socket->total_recv_bytes; int retval = port->onRead(reactor, port, event); + + long socket_recv_bytes = event->socket->total_recv_bytes - last_recv_bytes; + if (socket_recv_bytes > 0) { + sw_atomic_fetch_add(&port->gs->total_recv_bytes, socket_recv_bytes); + sw_atomic_fetch_add(&serv->gs->total_recv_bytes, socket_recv_bytes); + } if (!conn->active) { return retval; } 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); } @@ -625,12 +589,12 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { return SW_ERR; } - swTraceLog(SW_TRACE_REACTOR, - "fd=%d, conn->close_notify=%d, serv->disable_notify=%d, conn->close_force=%d", - fd, - conn->close_notify, - serv->disable_notify, - conn->close_force); + swoole_trace_log(SW_TRACE_REACTOR, + "fd=%d, conn->close_notify=%d, serv->disable_notify=%d, conn->close_force=%d", + fd, + conn->close_notify, + serv->disable_notify, + conn->close_force); if (conn->close_notify) { #ifdef SW_USE_OPENSSL @@ -692,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) { - swError("calloc[1] failed"); - return SW_ERR; - } - reactor_pipe_num = worker_num / reactor_num; - return SW_OK; -} - /** * [master] */ @@ -717,13 +664,7 @@ int Server::start_reactor_threads() { return SW_ERR; } - Reactor *reactor = SwooleTG.reactor; - -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swSignalfd_setup(reactor); - } -#endif + Reactor *reactor = sw_reactor(); for (auto iter = ports.begin(); iter != ports.end(); iter++) { auto port = *iter; @@ -737,16 +678,10 @@ int Server::start_reactor_threads() { reactor->add(port->socket, SW_EVENT_READ); } - /** - * create reactor thread - */ - ReactorThread *thread; - int i; - store_listen_socket(); if (single_thread) { - ReactorThread_init(this, reactor, 0); + get_thread(0)->init(this, reactor, 0); goto _init_master_thread; } /** @@ -760,20 +695,13 @@ int Server::start_reactor_threads() { SwooleTG.id = reactor_num; } -#ifdef HAVE_PTHREAD_BARRIER - // init thread barrier - pthread_barrier_init(&barrier, nullptr, reactor_num + 1); -#endif - for (i = 0; i < reactor_num; i++) { - thread = &(reactor_threads[i]); - thread->thread = std::thread(ReactorThread_loop, this, i); - } -#ifdef HAVE_PTHREAD_BARRIER - // wait reactor thread - pthread_barrier_wait(&barrier); -#else - SW_START_SLEEP; -#endif + SW_LOOP_N(reactor_num) { + get_thread(i)->thread = std::thread([=]() { + swoole_thread_init(); + reactor_thread_main_loop(this, i); + swoole_thread_clean(); + }); + } _init_master_thread: @@ -784,52 +712,15 @@ int Server::start_reactor_threads() { start_heartbeat_thread(); } - SwooleTG.type = SW_THREAD_MASTER; - SwooleTG.update_time = 1; - SwooleTG.reactor = reactor; - - if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) { - SwooleTG.timer->reinit(reactor); - } - - SwooleG.pid = getpid(); - SwooleG.process_type = SW_PROCESS_MASTER; - - reactor->ptr = this; - reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); - - if (hooks[Server::HOOK_MASTER_START]) { - call_hook(Server::HOOK_MASTER_START, this); - } - - /** - * 1 second timer - */ - if ((master_timer = swoole_timer_add(1000, true, Server::timer_callback, this)) == nullptr) { - swoole_event_free(); - return SW_ERR; - } - - if (onStart) { - onStart(this); - } - - return swoole_event_wait(); + return start_master_thread(reactor); } -static int ReactorThread_init(Server *serv, Reactor *reactor, uint16_t reactor_id) { - ReactorThread *thread = serv->get_thread(reactor_id); - +int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { reactor->ptr = serv; reactor->id = 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, [thread](Reactor *reactor, int &event_num) -> bool { - return event_num == (int) thread->pipe_num; - }); - reactor->default_error_handler = ReactorThread_onClose; reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorThread_onPipeRead); @@ -862,97 +753,84 @@ static int ReactorThread_init(Server *serv, Reactor *reactor, uint16_t reactor_i } 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; - thread->pipe_sockets = (Socket *) sw_calloc(max_pipe_fd, sizeof(Socket)); - if (!thread->pipe_sockets) { - swSysError("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); } - for (uint32_t i = 0; i < serv->worker_num; i++) { - int pipe_fd = serv->workers[i].pipe_master->fd; - Socket *socket = &thread->pipe_sockets[pipe_fd]; + if (serv->pipe_command) { + 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; + } - socket->fd = pipe_fd; - socket->fd_type = SW_FD_PIPE; - socket->buffer_size = UINT_MAX; + 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()) { + return SW_ERR; + } + SW_LOOP_N(serv->worker_num) { 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; } - if (thread->notify_pipe == nullptr) { - thread->notify_pipe = serv->workers[i].pipe_worker; + /** + * 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; } - thread->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 = SW_THREAD_REACTOR; + 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); - 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)) { - swSysWarn("pthread_setaffinity_np() failed"); - } + if (serv->is_thread_mode()) { + serv->call_worker_start_callback(serv->get_worker(reactor_id)); } -#endif - - swSignal_none(); - if (ReactorThread_init(serv, reactor, reactor_id) < 0) { + Reactor *reactor = sw_reactor(); + if (thread->init(serv, reactor, reactor_id) < 0) { return; } // wait other thread -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_wait(&serv->barrier); -#else - SW_START_SLEEP; -#endif + if (serv->is_process_mode()) { + serv->reactor_thread_barrier.wait(); + } // main loop swoole_event_wait(); - - for (auto it = thread->send_buffers.begin(); it != thread->send_buffers.end(); it++) { - delete it->second; + if (serv->is_thread_mode()) { + serv->call_worker_stop_callback(serv->get_worker(reactor_id)); } - sw_free(thread->pipe_sockets); + thread->clean(); } static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) { @@ -963,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; } @@ -976,82 +854,76 @@ 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; Connection *conn = (Connection *) _socket->object; + ListenPort *port = serv->get_port_by_fd(conn->fd); 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; - swTrace("send string package, size=%ld bytes", (long) length); + swoole_trace("dispatch task, size=%u bytes", rdata->info.len); - if (serv->stream_socket_file) { - Stream *stream = Stream::create(serv->stream_socket_file, 0, SW_SOCK_UNIX_STREAM); - if (!stream) { - return SW_ERR; - } - stream->response = ReactorThread_onStreamResponse; - stream->private_data = serv; - stream->private_data_2 = conn; - stream->private_data_fd = conn->session_id; - ListenPort *port = serv->get_port_by_fd(conn->fd); - stream->set_max_length(port->protocol.package_max_length); + task.info.fd = conn->fd; + task.info.len = rdata->info.len; + task.data = rdata->data; - 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 SW_ERR; - } - 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_OK; + return SW_ERR; } else { - task.info.fd = conn->fd; - task.info.len = length; - task.data = data; - if (!serv->factory->dispatch(&task)) { - return SW_ERR; - } - if (length > 0) { - sw_atomic_fetch_add(&conn->recv_queued_bytes, length); - swTraceLog(SW_TRACE_SERVER, "[Master] len=%d, qb=%d\n", length, conn->recv_queued_bytes); + 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; } } -void Server::join_reactor_thread() { - if (single_thread) { - return; - } - ReactorThread *thread; +void Server::join_heartbeat_thread() { /** * Shutdown heartbeat thread */ if (heartbeat_thread.joinable()) { - swTraceLog(SW_TRACE_SERVER, "terminate heartbeat thread"); + swoole_trace_log(SW_TRACE_SERVER, "terminate heartbeat thread"); if (pthread_cancel(heartbeat_thread.native_handle()) < 0) { - swSysWarn("pthread_cancel(%ld) failed", (ulong_t) heartbeat_thread.native_handle()); + swoole_sys_warning("pthread_cancel(%ld) failed", (ulong_t) heartbeat_thread.native_handle()); } // 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; @@ -1061,28 +933,19 @@ void Server::join_reactor_thread() { } else { _cancel: if (pthread_cancel(thread->thread.native_handle()) < 0) { - swSysWarn("pthread_cancel(%ld) failed", (long) thread->thread.native_handle()); + swoole_sys_warning("pthread_cancel(%ld) failed", (long) thread->thread.native_handle()); } } thread->thread.join(); } } -void Server::destroy_reactor_threads() { - sw_shm_free(connection_list); - delete[] reactor_threads; - - if (message_box) { - message_box->destroy(); - } -} - void Server::start_heartbeat_thread() { heartbeat_thread = std::thread([this]() { - swSignal_none(); + swoole_signal_block_all(); - SwooleTG.type = SW_THREAD_HEARTBEAT; - SwooleTG.id = reactor_num; + SwooleTG.type = THREAD_HEARTBEAT; + SwooleTG.id = reactor_num + 1; while (running) { double now = microtime(); @@ -1098,8 +961,7 @@ void Server::start_heartbeat_thread() { ev.type = SW_SERVER_EVENT_CLOSE_FORCE; // convert fd to session_id, in order to verify the connection before the force close connection ev.fd = session_id; - Socket *_pipe_sock = get_reactor_thread_pipe(session_id, conn->reactor_id); - _pipe_sock->send_blocking((void *) &ev, sizeof(ev)); + get_reactor_pipe_socket(session_id, conn->reactor_id)->send_blocking(&ev, sizeof(ev)); }); sleep(heartbeat_check_interval); } diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 7ff13f553e2..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 = swHttp_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 8851d882cc2..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 @@ -39,35 +39,84 @@ void Server::init_task_workers() { * Make the task worker support asynchronous */ if (task_enable_coroutine) { - if (task_ipc_mode == SW_TASK_IPC_MSGQUEUE || task_ipc_mode == SW_TASK_IPC_PREEMPTIVE) { - swError("cannot use msgqueue when task_enable_coroutine is enable"); + if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) { + swoole_error("cannot use msgqueue when task_enable_coroutine is enable"); return; } pool->main_loop = TaskWorker_loop_async; } - if (task_ipc_mode == SW_TASK_IPC_PREEMPTIVE) { - pool->dispatch_mode = SW_DISPATCH_QUEUE; + 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, Worker *worker, EventData *req) { + Server *serv = (Server *) pool->ptr; + 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); + return SW_OK; + } + + Server::Command::Handler handler = iter->second; + PacketPtr packet; + if (!Server::task_unpack(req, sw_tg_buffer(), &packet)) { + return SW_OK; + } + + auto result = handler(serv, std::string(packet.data, packet.length)); + + SendData task{}; + 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(); + task.data = result.c_str(); + + 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, 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 EventData::pack(const void *_data, size_t _length) { - if (_length < SW_IPC_MAX_SIZE - sizeof(info)) { - memcpy(data, _data, _length); - info.len = _length; +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 = swoole_get_process_id(); + task->info.time = microtime(); + + if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { + memcpy(task->data, _data, _length); + task->info.len = _length; return true; } @@ -78,26 +127,85 @@ bool EventData::pack(const void *_data, size_t _length) { } if (file.write_all(_data, _length) != _length) { - swWarn("write to tmpfile failed"); + swoole_warning("write to tmpfile failed"); return false; } - info.len = sizeof(pkg); - swTask_type(this) |= SW_TASK_TMPFILE; + task->info.len = sizeof(pkg); + task->info.ext_flags |= SW_TASK_TMPFILE; swoole_strlcpy(pkg.tmpfile, file.get_path().c_str(), sizeof(pkg.tmpfile)); pkg.length = _length; - memcpy(data, &pkg, sizeof(pkg)); + memcpy(task->data, &pkg, sizeof(pkg)); return true; } -bool EventData::unpack(String *buffer) { +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; + packet->length = task->info.len; + return true; + } + PacketTask _pkg{}; - memcpy(&_pkg, data, sizeof(_pkg)); + memcpy(&_pkg, task->data, sizeof(_pkg) - 1); File fp(_pkg.tmpfile, O_RDONLY); if (!fp.ready()) { - swSysWarn("open(%s) failed", _pkg.tmpfile); + swoole_sys_warning("open(%s) failed", _pkg.tmpfile); return false; } if (buffer->size < _pkg.length && !buffer->extend(_pkg.length)) { @@ -106,39 +214,40 @@ bool EventData::unpack(String *buffer) { if (fp.read_all(buffer->str, _pkg.length) != _pkg.length) { return false; } - if (!(swTask_type(this) & SW_TASK_PEEK)) { + if (!(task->info.ext_flags & SW_TASK_PEEK)) { unlink(_pkg.tmpfile); } buffer->length = _pkg.length; + packet->data = buffer->str; + packet->length = buffer->length; return true; } static void TaskWorker_signal_init(ProcessPool *pool) { - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - - swSignal_set(SIGHUP, nullptr); - swSignal_set(SIGPIPE, nullptr); - swSignal_set(SIGUSR1, Server::worker_signal_handler); - swSignal_set(SIGUSR2, nullptr); - swSignal_set(SIGTERM, Server::worker_signal_handler); + 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); + swoole_signal_set(SIGUSR2, nullptr); + swoole_signal_set(SIGTERM, Server::worker_signal_handler); #ifdef SIGRTMIN - swSignal_set(SIGRTMIN, Server::worker_signal_handler); + swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); #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 */ if (serv->task_enable_coroutine) { if (swoole_event_init(0) < 0) { - swError("[TaskWorker] create reactor failed"); + swoole_error("[TaskWorker] create reactor failed"); return; } SwooleG.enable_signalfd = 1; @@ -148,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); } /** @@ -178,21 +275,18 @@ 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; } else { - swSysWarn("read(%d, %ld) failed", event->fd, sizeof(task)); + swoole_sys_warning("read(%d, %ld) failed", event->fd, sizeof(task)); return SW_ERR; } } @@ -203,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; @@ -222,62 +316,58 @@ 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) { - swWarn("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]"); - return SW_ERR; + swoole_warning("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]"); + return false; } if (current_task == nullptr) { current_task = last_task; } if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { - swWarn("Server::task()/Server::finish() is not supported in onPipeMessage callback"); - return SW_ERR; + swoole_warning("Server::task()/Server::finish() is not supported in onPipeMessage callback"); + return false; } - if (swTask_type(current_task) & SW_TASK_NOREPLY) { - swWarn("Server::finish() can only be used in the worker process"); - return SW_ERR; + if (current_task->info.ext_flags & SW_TASK_NOREPLY) { + swoole_warning("Server::finish() can only be used in the worker process"); + return false; } uint16_t source_worker_id = current_task->info.reactor_id; Worker *worker = get_worker(source_worker_id); if (worker == nullptr) { - swWarn("invalid worker_id[%d]", source_worker_id); - return SW_ERR; + swoole_warning("invalid worker_id[%d]", source_worker_id); + return false; } - int ret; + ssize_t retval; // for swoole_server_task - if (swTask_type(current_task) & SW_TASK_NONBLOCK) { - buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; - buf.info.time = microtime(); - buf.info.reactor_id = SwooleWG.worker->id; + 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 false; + } // callback function - if (swTask_type(current_task) & SW_TASK_CALLBACK) { + if (current_task->info.ext_flags & SW_TASK_CALLBACK) { flags |= SW_TASK_CALLBACK; - } else if (swTask_type(current_task) & SW_TASK_COROUTINE) { + } else if (current_task->info.ext_flags & SW_TASK_COROUTINE) { flags |= SW_TASK_COROUTINE; } - swTask_type(&buf) = flags; - - // write to file - if (!buf.pack(data, data_len)) { - swWarn("large task pack failed()"); - return SW_ERR; - } + buf.info.ext_flags |= flags; + buf.info.type = SW_SERVER_EVENT_FINISH; + 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; @@ -285,49 +375,50 @@ 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 worker->lock->lock(); - if (swTask_type(current_task) & SW_TASK_WAITALL) { + if (current_task->info.ext_flags & SW_TASK_WAITALL) { sw_atomic_t *finish_count = (sw_atomic_t *) result->data; char *_tmpfile = result->data + 4; File file(_tmpfile, O_APPEND | O_WRONLY); if (file.ready()) { - buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; - swTask_type(&buf) = flags; - if (!buf.pack(data, data_len)) { - swWarn("large task pack failed()"); + EventData buf; + if (!task_pack(&buf, data, data_len)) { + swoole_warning("large task pack failed()"); buf.info.len = 0; } - size_t bytes = sizeof(buf.info) + buf.info.len; + buf.info.ext_flags |= flags; + buf.info.type = SW_SERVER_EVENT_FINISH; + buf.info.fd = get_task_id(current_task); + size_t bytes = buf.size(); if (file.write_all(&buf, bytes) != bytes) { - swSysWarn("write(%s, %ld) failed", _tmpfile, bytes); + swoole_sys_warning("write(%s, %ld) failed", _tmpfile, bytes); } sw_atomic_fetch_add(finish_count, 1); } } else { - result->info.type = SW_SERVER_EVENT_FINISH; - result->info.fd = current_task->info.fd; - swTask_type(result) = flags; - if (!result->pack(data, data_len)) { + if (!task_pack(result, data, data_len)) { // unlock worker worker->lock->unlock(); - swWarn("large task pack failed()"); - return SW_ERR; + swoole_warning("large task pack failed()"); + return false; } + result->info.ext_flags |= flags; + result->info.type = SW_SERVER_EVENT_FINISH; + 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; } @@ -335,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) { - swWarn("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 { - swSysWarn("send result to worker failed"); + 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 b00c06ecdbc..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,58 +22,48 @@ #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; - - swSignal_set(SIGHUP, nullptr); - swSignal_set(SIGPIPE, SIG_IGN); - swSignal_set(SIGUSR1, nullptr); - swSignal_set(SIGUSR2, nullptr); + if (is_thread_mode()) { + return; + } + swoole_signal_set(SIGHUP, nullptr); + swoole_signal_set(SIGPIPE, SIG_IGN); + swoole_signal_set(SIGUSR1, nullptr); + swoole_signal_set(SIGUSR2, nullptr); // swSignal_set(SIGINT, Server::worker_signal_handler); - swSignal_set(SIGTERM, Server::worker_signal_handler); + swoole_signal_set(SIGTERM, Server::worker_signal_handler); // for test - swSignal_set(SIGVTALRM, Server::worker_signal_handler); + swoole_signal_set(SIGVTALRM, Server::worker_signal_handler); #ifdef SIGRTMIN - swSignal_set(SIGRTMIN, Server::worker_signal_handler); + swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); #endif } 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 case SIGVTALRM: - swWarn("SIGVTALRM coming"); + swoole_warning("SIGVTALRM coming"); break; case SIGUSR1: case SIGUSR2: @@ -91,7 +81,7 @@ void Server::worker_signal_handler(int signo) { } } -static sw_inline bool Worker_discard_data(Server *serv, Connection *conn, EventData *task) { +static sw_inline bool Worker_discard_data(Server *serv, Connection *conn, DataHead *info) { if (conn == nullptr) { if (serv->disable_notify && !serv->discard_timeout_request) { return false; @@ -104,213 +94,133 @@ static sw_inline bool Worker_discard_data(Server *serv, Connection *conn, EventD return false; } } -_discard_data : { +_discard_data: swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, "[2] ignore data[%u bytes] received from session#%ld", - task->info.len, - task->info.fd); -} + info->len, + info->fd); 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: - swSysWarn("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; - - /** - * passing memory pointer - */ - PacketPtr task{}; - memcpy(&task.info, data + 4, sizeof(task.info)); - task.info.flags = SW_EVENT_DATA_PTR; - task.data.length = length - (uint32_t) sizeof(task.info) - 4; - task.data.str = (char *) (data + 4 + sizeof(task.info)); - - /** - * do task - */ - serv->last_stream_socket = sock; - serv->accept_task((EventData *) &task); - serv->last_stream_socket = nullptr; - - /** - * stream end - */ - int _end = 0; - SwooleTG.reactor->write(SwooleTG.reactor, sock, (void *) &_end, sizeof(_end)); - - return SW_OK; -} - typedef std::function TaskCallback; -static sw_inline void Worker_do_task(Server *serv, Worker *worker, EventData *task, const TaskCallback &callback) { +static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { RecvData recv_data; - recv_data.info = task->info; - recv_data.info.len = serv->get_packet(task, const_cast(&recv_data.data)); + 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); } } -int Server::accept_task(EventData *task) { - Worker *worker = SwooleWG.worker; - // worker busy - worker->status = SW_WORKER_BUSY; +void Server::worker_accept_event(DataHead *info) { + Worker *worker = sw_worker(); + worker->set_status_to_busy(); - switch (task->info.type) { + switch (info->type) { case SW_SERVER_EVENT_RECV_DATA: { - Connection *conn = get_connection_verify(task->info.fd); + Connection *conn = get_connection_verify(info->fd); if (conn) { - if (task->info.len > 0) { - sw_atomic_fetch_sub(&conn->recv_queued_bytes, task->info.len); - swTraceLog(SW_TRACE_SERVER, "[Worker] len=%d, qb=%d\n", task->info.len, conn->recv_queued_bytes); + if (info->len > 0) { + 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=%lu, qb=%d", + conn->session_id, + packet.length, + conn->recv_queued_bytes); } - conn->last_dispatch_time = task->info.time; + conn->last_dispatch_time = info->time; } - if (!Worker_discard_data(this, conn, task)) { - Worker_do_task(this, worker, task, onReceive); + if (!Worker_discard_data(this, conn, info)) { + Worker_do_task(this, worker, info, onReceive); } break; } case SW_SERVER_EVENT_RECV_DGRAM: { - Worker_do_task(this, worker, task, onPacket); + Worker_do_task(this, worker, info, onPacket); break; } case SW_SERVER_EVENT_CLOSE: { #ifdef SW_USE_OPENSSL - Connection *conn = get_connection_verify_no_ssl(task->info.fd); + Connection *conn = get_connection_verify_no_ssl(info->fd); if (conn && conn->ssl_client_cert && conn->ssl_client_cert_pid == SwooleG.pid) { delete conn->ssl_client_cert; conn->ssl_client_cert = nullptr; } #endif - factory->end(task->info.fd, false); + factory->end(info->fd, false); break; } case SW_SERVER_EVENT_CONNECT: { #ifdef SW_USE_OPENSSL // SSL client certificate - if (task->info.len > 0) { - Connection *conn = get_connection_verify_no_ssl(task->info.fd); + if (info->len > 0) { + Connection *conn = get_connection_verify_no_ssl(info->fd); if (conn) { - char *cert_data = nullptr; - size_t length = get_packet(task, &cert_data); - conn->ssl_client_cert = new String(cert_data, length); + 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; } } #endif if (onConnect) { - onConnect(this, &task->info); + onConnect(this, info); } break; } case SW_SERVER_EVENT_BUFFER_FULL: { if (onBufferFull) { - onBufferFull(this, &task->info); + onBufferFull(this, info); } break; } case SW_SERVER_EVENT_BUFFER_EMPTY: { if (onBufferEmpty) { - onBufferEmpty(this, &task->info); + onBufferEmpty(this, info); } break; } case SW_SERVER_EVENT_FINISH: { - onFinish(this, task); + onFinish(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_PIPE_MESSAGE: { - onPipeMessage(this, task); + 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: - swWarn("[Worker] error event[type=%d]", (int) task->info.type); + 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); } - return SW_OK; } -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(); @@ -322,43 +232,43 @@ void Server::worker_start_callback() { if (!group_.empty()) { _group = getgrnam(group_.c_str()); if (!_group) { - swWarn("get group [%s] info failed", group_.c_str()); + swoole_warning("get group [%s] info failed", group_.c_str()); } } // get user info if (!user_.empty()) { _passwd = getpwnam(user_.c_str()); if (!_passwd) { - swWarn("get user [%s] info failed", user_.c_str()); + swoole_warning("get user [%s] info failed", user_.c_str()); } } // set process group if (_group && setgid(_group->gr_gid) < 0) { - swSysWarn("setgid to [%s] failed", group_.c_str()); + swoole_sys_warning("setgid to [%s] failed", group_.c_str()); } // set process user if (_passwd && setuid(_passwd->pw_uid) < 0) { - swSysWarn("setuid to [%s] failed", user_.c_str()); + swoole_sys_warning("setuid to [%s] failed", user_.c_str()); } // chroot if (!chroot_.empty()) { if (::chroot(chroot_.c_str()) == 0) { if (chdir("/") < 0) { - swSysWarn("chdir(\"/\") failed"); + swoole_sys_warning("chdir(\"/\") failed"); } } else { - swSysWarn("chroot(\"%s\") failed", chroot_.c_str()); + swoole_sys_warning("chroot(\"%s\") failed", chroot_.c_str()); } } } - for (uint32_t i = 0; i < worker_num + task_worker_num; i++) { - Worker *worker = get_worker(i); - if (SwooleG.process_id == i) { + SW_LOOP_N(worker_num + task_worker_num) { + 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(); } } @@ -366,54 +276,117 @@ void Server::worker_start_callback() { sw_logger()->reopen(); } - SwooleWG.worker = get_worker(SwooleG.process_id); - SwooleWG.worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); if (is_process_mode()) { sw_shm_protect(session_list, PROT_READ); - /** - * Use only the first block of pipe_buffer memory in worker process - */ - for (uint32_t i = 1; i < reactor_num; i++) { - sw_free(pipe_buffers[i]); - } } -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && SwooleTG.reactor && SwooleG.signal_fd == 0) { - swSignalfd_setup(SwooleTG.reactor); + call_worker_start_callback(worker); +} + +void Server::worker_stop_callback(Worker *worker) { + call_worker_stop_callback(worker); +} + +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 (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args); } -#endif + if (isset_hook(HOOK_WORKER_START)) { + call_hook(Server::HOOK_WORKER_START, hook_args); + } + + swoole_clear_last_error(); + swoole_clear_last_error_msg(); - call_worker_start_callback(SwooleWG.worker); + if (onWorkerStart) { + onWorkerStart(this, worker); + } } -void Server::worker_stop_callback() { +void Server::call_worker_stop_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_STOP)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } if (onWorkerStop) { - onWorkerStop(this, SwooleG.process_id); + onWorkerStop(this, worker); } - if (!worker_buffers.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"); - worker_buffers.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; } @@ -424,22 +397,24 @@ 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); } if (is_base_mode()) { if (is_worker()) { + if (worker->id == 0 && gs->event_workers.running == 0) { + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); + } + if (onBeforeShutdown) { + onBeforeShutdown(this); + } + } for (auto ls : ports) { reactor->del(ls->socket); } @@ -453,17 +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; - // send message to manager - if (message_box && message_box->push(&msg, sizeof(msg)) < 0) { - running = 0; - } else { - swoole_kill(gs->manager_pid, SIGIO); + if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 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 == SW_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 == SW_DISPATCH_CO_CONN_LB || dispatch_mode == SW_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; @@ -596,7 +579,7 @@ int Server::start_event_worker(Worker *worker) { * [Worker/TaskWorker/Master] Send data to ReactorThread */ ssize_t Server::send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id) { - Socket *pipe_sock = get_reactor_thread_pipe(session_id, ev_data->info.reactor_id); + Socket *pipe_sock = get_reactor_pipe_socket(session_id, ev_data->info.reactor_id); if (swoole_event_is_available()) { return swoole_event_write(pipe_sock, ev_data, sendn); } else { @@ -604,15 +587,6 @@ ssize_t Server::send_to_reactor_thread(const EventData *ev_data, size_t sendn, S } } -ssize_t Server::send_to_reactor_thread(const DataHead *head, const iovec *iov, size_t iovcnt, SessionId session_id) { - Socket *pipe_sock = get_reactor_thread_pipe(session_id, head->reactor_id); - if (swoole_event_is_available()) { - return swoole_event_writev(pipe_sock, iov, iovcnt); - } else { - return pipe_sock->writev_blocking(iov, iovcnt); - } -} - /** * send message from worker to another worker */ @@ -624,121 +598,17 @@ ssize_t Server::send_to_worker_from_worker(Worker *dst_worker, const void *buf, * receive data from reactor */ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { - ssize_t recv_n = 0; Server *serv = (Server *) reactor->ptr; - PipeBuffer *pipe_buffer = serv->pipe_buffers[0]; - struct iovec buffers[2]; - int recv_chunk_count = 0; - DataHead *info = &pipe_buffer->info; - -_read_from_pipe: - recv_n = recv(event->fd, info, sizeof(pipe_buffer->info), MSG_PEEK); - if (recv_n < 0) { - if (event->socket->catch_error(errno) == SW_WAIT) { - return SW_OK; - } - return SW_ERR; - } + PipeBuffer *pipe_buffer = serv->get_worker_message_bus()->get_buffer(); - if (pipe_buffer->info.flags & SW_EVENT_DATA_CHUNK) { - String *worker_buffer = serv->get_worker_buffer(info); - if (worker_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", - info->msg_id, - event->fd, - info->reactor_id); - return SW_OK; - } - size_t remain_len = pipe_buffer->info.len - worker_buffer->length; - - buffers[0].iov_base = info; - buffers[0].iov_len = sizeof(pipe_buffer->info); - buffers[1].iov_base = worker_buffer->str + worker_buffer->length; - buffers[1].iov_len = SW_MIN(serv->ipc_max_size - sizeof(pipe_buffer->info), remain_len); - - recv_n = readv(event->fd, buffers, 2); - if (recv_n == 0) { - swWarn("receive pipeline data error, pipe_fd=%d, reactor_id=%d", event->fd, info->reactor_id); - return SW_ERR; - } - if (recv_n < 0 && event->socket->catch_error(errno) == SW_WAIT) { - return SW_OK; - } - if (recv_n > 0) { - worker_buffer->length += (recv_n - sizeof(pipe_buffer->info)); - swTrace("append msgid=%ld, buffer=%p, n=%ld", pipe_buffer->info.msg_id, worker_buffer, recv_n); - } - - recv_chunk_count++; - - if (!(pipe_buffer->info.flags & SW_EVENT_DATA_END)) { - /** - * if the reactor thread sends too many chunks to the worker process, - * the worker process may receive chunks all the time, - * resulting in the worker process being unable to handle other tasks. - * in order to make the worker process handle tasks fairly, - * the maximum number of consecutive chunks received by the worker is limited. - */ - if (recv_chunk_count >= SW_WORKER_MAX_RECV_CHUNK_COUNT) { - swTraceLog(SW_TRACE_WORKER, - "worker process[%u] receives the chunk data to the maximum[%d], return to event loop", - SwooleG.process_id, - recv_chunk_count); - return SW_OK; - } - goto _read_from_pipe; - } else { - /** - * Because we don't want to split the EventData parameters into DataHead and data, - * we store the value of the worker_buffer pointer in EventData.data. - * The value of this pointer will be fetched in the Server_worker_get_packet function. - */ - pipe_buffer->info.flags |= SW_EVENT_DATA_OBJ_PTR; - memcpy(pipe_buffer->data, &worker_buffer, sizeof(worker_buffer)); - swTrace("msg_id=%ld, len=%u", pipe_buffer->info.msg_id, pipe_buffer->info.len); - } - } else { - recv_n = event->socket->read(pipe_buffer, serv->ipc_max_size); - } - - if (recv_n > 0 && serv->accept_task((EventData *) pipe_buffer) == SW_OK) { - if (pipe_buffer->info.flags & SW_EVENT_DATA_END) { - serv->worker_buffers.erase(pipe_buffer->info.msg_id); - } + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { return SW_OK; } - return SW_ERR; -} - -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; - } + serv->worker_accept_event(&pipe_buffer->info); + serv->get_worker_message_bus()->pop(); - if ((flags & SW_PIPE_NONBLOCK) && SwooleTG.reactor) { - return SwooleTG.reactor->write(SwooleTG.reactor, pipe_sock, buf, n); - } else { - return pipe_sock->send_blocking(buf, n); - } + return SW_OK; } + } // namespace swoole diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index f180864ab2b..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; @@ -104,9 +104,6 @@ void swoole_event_defer(Callback cb, void *private_data) { SwooleTG.reactor->defer(cb, private_data); } -/** - * @return SW_OK or SW_ERR - */ ssize_t swoole_event_write(Socket *socket, const void *data, size_t len) { return SwooleTG.reactor->write(SwooleTG.reactor, socket, data, len); } @@ -126,3 +123,11 @@ bool swoole_event_isset_handler(int fdtype) { 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 9226b1d5fda..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(); @@ -43,7 +51,7 @@ TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callb bool swoole_timer_del(TimerNode *tnode) { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return false; } return SwooleTG.timer->remove(tnode); @@ -51,7 +59,7 @@ bool swoole_timer_del(TimerNode *tnode) { void swoole_timer_delay(TimerNode *tnode, long delay_ms) { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return; } return SwooleTG.timer->delay(tnode, delay_ms); @@ -59,7 +67,7 @@ void swoole_timer_delay(TimerNode *tnode, long delay_ms) { long swoole_timer_after(long ms, const TimerCallback &callback, void *private_data) { if (ms <= 0) { - swWarn("Timer must be greater than 0"); + swoole_warning("Timer must be greater than 0"); return SW_ERR; } TimerNode *tnode = swoole_timer_add(ms, false, callback, private_data); @@ -72,7 +80,7 @@ long swoole_timer_after(long ms, const TimerCallback &callback, void *private_da long swoole_timer_tick(long ms, const TimerCallback &callback, void *private_data) { if (ms <= 0) { - swWarn("Timer must be greater than 0"); + swoole_warning("Timer must be greater than 0"); return SW_ERR; } TimerNode *tnode = swoole_timer_add(ms, true, callback, private_data); @@ -85,7 +93,7 @@ long swoole_timer_tick(long ms, const TimerCallback &callback, void *private_dat bool swoole_timer_exists(long timer_id) { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return false; } TimerNode *tnode = SwooleTG.timer->get(timer_id); @@ -94,7 +102,7 @@ bool swoole_timer_exists(long timer_id) { bool swoole_timer_clear(long timer_id) { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return false; } return SwooleTG.timer->remove(SwooleTG.timer->get(timer_id)); @@ -102,7 +110,7 @@ bool swoole_timer_clear(long timer_id) { TimerNode *swoole_timer_get(long timer_id) { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return nullptr; } return SwooleTG.timer->get(timer_id); @@ -110,17 +118,15 @@ TimerNode *swoole_timer_get(long timer_id) { void swoole_timer_free() { if (!swoole_timer_is_available()) { - swWarn("timer is not available"); + swoole_warning("timer is not available"); return; } delete SwooleTG.timer; SwooleTG.timer = nullptr; - SwooleG.signal_alarm = false; } int swoole_timer_select() { if (!swoole_timer_is_available()) { - swWarn("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 98c954b799e..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]; @@ -798,4 +824,51 @@ function swoole_get_variance($avg, $array, $is_swatch = false) function swoole_get_average($array) { return array_sum($array) / count($array); -} \ No newline at end of file +} + +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 47899b5ae29..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.1", - "source": { - "type": "git", - "url": "https://github.com/php-http/message.git", - "reference": "887734d9c515ad9a564f6581a682fff87a6253cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/887734d9c515ad9a564f6581a682fff87a6253cc", - "reference": "887734d9c515ad9a564f6581a682fff87a6253cc", - "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.1" - }, - "time": "2021-05-24T18:11:08+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.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "fde4bdb10bf197f932ebccfcb9982881d296fc4c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/fde4bdb10bf197f932ebccfcb9982881d296fc4c", - "reference": "fde4bdb10bf197f932ebccfcb9982881d296fc4c", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1.0", - "symfony/deprecation-contracts": "^2.1", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.15", - "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.3" - }, - "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-06-24T08:13:00+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.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/162e886ca035869866d233a2bfef70cc28f9bbe5", - "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5", - "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.15" - }, - "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.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-05-26T17:43:10+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.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0", - "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0", - "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.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/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 5df7b009c3f..9d4a99d40f1 100644 --- a/tests/include/lib/src/responder/get.php +++ b/tests/include/lib/src/responder/get.php @@ -1,71 +1,79 @@ + echo "Hello World!\n"; + echo "Hello World!"; + break; +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index a255ce6a236..ee3477d5a5e 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,19 @@ 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); +} 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/dnslookup_query_hosts.phpt b/tests/swoole_coroutine/dnslookup_query_hosts.phpt new file mode 100644 index 00000000000..3cadd8eb15a --- /dev/null +++ b/tests/swoole_coroutine/dnslookup_query_hosts.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_coroutine: dnslookup query hosts +--SKIPIF-- + +--FILE-- + +--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/1.phpt b/tests/swoole_coroutine/join/1.phpt new file mode 100644 index 00000000000..3461ff26421 --- /dev/null +++ b/tests/swoole_coroutine/join/1.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_coroutine/join: 1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +ALL DONE diff --git a/tests/swoole_coroutine/join/2.phpt b/tests/swoole_coroutine/join/2.phpt new file mode 100644 index 00000000000..c3dcc76bd04 --- /dev/null +++ b/tests/swoole_coroutine/join/2.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine/join: 2 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +ALL DONE diff --git a/tests/swoole_coroutine/join/3.phpt b/tests/swoole_coroutine/join/3.phpt new file mode 100644 index 00000000000..06f390fdbc1 --- /dev/null +++ b/tests/swoole_coroutine/join/3.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_coroutine/join: 3 +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_coroutine/join/4.phpt b/tests/swoole_coroutine/join/4.phpt new file mode 100644 index 00000000000..c9d00029883 --- /dev/null +++ b/tests/swoole_coroutine/join/4.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine/join: 4 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DEFER CALLBACK +DONE 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 new file mode 100644 index 00000000000..6ea973d2e47 --- /dev/null +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_curl/multi: guzzle +--SKIPIF-- + +--FILE-- + $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']; + } + + 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, 3.0); + } + + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done 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_curl/upload/2.phpt b/tests/swoole_curl/upload/2.phpt new file mode 100644 index 00000000000..9277dd18295 --- /dev/null +++ b/tests/swoole_curl/upload/2.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/upload: CURL file uploading +--INI-- +--SKIPIF-- + +--FILE-- + $file)); + $result = curl_exec($ch); + Assert::notEmpty($result); + $json = json_decode($result); + Assert::notEmpty($json); + Assert::notEmpty($json->files->swoole_file); + $prefix = 'data:application/octet-stream;base64,'; + Assert::startsWith($json->files->swoole_file, $prefix); + $data = substr($json->files->swoole_file, strlen($prefix)); + Assert::eq(md5(base64_decode($data)), md5_file(TEST_IMAGE)); +}); +?> +--EXPECTF-- diff --git a/tests/swoole_curl/upload/3.phpt b/tests/swoole_curl/upload/3.phpt new file mode 100644 index 00000000000..a6fc94ad7c4 --- /dev/null +++ b/tests/swoole_curl/upload/3.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_curl/upload: CURL file uploading[INFILE] +--INI-- +--SKIPIF-- + +--FILE-- +run(function ($host) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=input"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_PUT, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $fp = fopen(TEST_IMAGE, 'r'); + curl_setopt($ch, CURLOPT_INFILE, $fp); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:")); + curl_setopt($ch, CURLOPT_INFILESIZE, filesize(TEST_IMAGE)); + + $http_result = curl_exec($ch); + Assert::eq(md5($http_result), md5_file(TEST_IMAGE)); +}); + +?> +--EXPECTF-- 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 new file mode 100644 index 00000000000..34f2581283c --- /dev/null +++ b/tests/swoole_http2_server/issue_4365.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_http2_server: github issue#4365 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + if (Assert::assert(!empty($res = `curl -s --http2-prior-knowledge http://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null`))) { + Assert::length($res, N); + 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([ + 'open_http2_protocol' => true, + 'enable_reuse_port' => true, + 'enable_coroutine' => false, + 'log_level' => 1, + 'log_file' => TEST_LOG_FILE, + ]); + $http->on('request', function ($request, $response) { + $response->end(str_repeat('x', N)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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/ssl_1.phpt b/tests/swoole_http_client_coro/websocket/ssl_1.phpt index eed5932b4ea..49514e592b1 100644 --- a/tests/swoole_http_client_coro/websocket/ssl_1.phpt +++ b/tests/swoole_http_client_coro/websocket/ssl_1.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_http_client_coro/websocket: ssl recv --SKIPIF-- - + --FILE-- 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 new file mode 100644 index 00000000000..fc9e50501b9 --- /dev/null +++ b/tests/swoole_http_server/upload4.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_http_server: upload 04 +--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)); + + $cfile = curl_file_create($file); + $cfile->setPostFilename(FILENAME); + $post_data['file'] = $cfile; + + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //POST数据 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $res = curl_exec($ch); + Assert::assert(!empty($res)); + Assert::eq($res, FILENAME); + curl_close($ch); + + $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 (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end($request->files['file']['name']); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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..ec6ef0c1750 --- /dev/null +++ b/tests/swoole_iouring/mix.phpt @@ -0,0 +1,193 @@ +--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_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/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 new file mode 100644 index 00000000000..1eaa823f2c6 --- /dev/null +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_runtime/file_hook: bug #4372 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS +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/open_basedir.phpt b/tests/swoole_runtime/file_hook/open_basedir.phpt new file mode 100644 index 00000000000..4ad814e0cbd --- /dev/null +++ b/tests/swoole_runtime/file_hook/open_basedir.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_runtime/file_hook: support open_basedir config +--SKIPIF-- + +--FILE-- + +--EXPECTF-- + +Warning: mkdir(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line 7 +bool(true) +bool(true) +bool(true) 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 968c10655c8..fbb59ec447f 100644 --- a/tests/swoole_server/bug_11000_01.phpt +++ b/tests/swoole_server/bug_11000_01.phpt @@ -5,16 +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); - var_dump($serv->stats()); + $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']); @@ -25,32 +31,4 @@ $pm->childFunc = function () { $pm->childFirst(); $pm->run(); ?> ---EXPECTF-- -array(13) { - ["start_time"]=> - int(%d) - ["connection_num"]=> - int(0) - ["accept_count"]=> - int(0) - ["close_count"]=> - int(0) - ["worker_num"]=> - int(2) - ["idle_worker_num"]=> - int(2) - ["task_worker_num"]=> - int(0) - ["tasking_num"]=> - int(0) - ["request_count"]=> - int(0) - ["dispatch_count"]=> - int(0) - ["worker_request_count"]=> - int(0) - ["worker_dispatch_count"]=> - int(0) - ["coroutine_num"]=> - int(0) -} +--EXPECT-- 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 2708565ae5f..253bb68defb 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -6,33 +6,37 @@ swoole_server: bug Github#2308 parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new \Swoole\Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $server = new Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP); $server->set([ '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(); - $redis->connect('127.0.0.1', 6379); - while (true) { - $msg = $redis->subscribe(['task']); - } + Swoole\Coroutine::create(function () { + $redis = new \Redis(); + $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); + $ret = $redis->set('foo', 'bar'); + Assert::assert($ret); + $ret = $redis->get('foo'); + Assert::same($ret, 'bar'); }); }); $server->on('workerStart', function ($server) { - $server->after(1000, function () use ($server) { + Swoole\Timer::after(1000, function () use ($server) { $server->stop(); }); }); - $server->on('Receive', function (swoole_server $server, int $fd, int $reactor_id, string $data) { + $server->on('Receive', function (Server $server, int $fd, int $reactor_id, string $data) { }); $server->start(); }; @@ -41,4 +45,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Fatal error: Swoole\Coroutine::create(): Unable to use async-io in manager process in %s on line %d 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/close_reset.phpt b/tests/swoole_server/close_reset.phpt new file mode 100644 index 00000000000..4dcfdd3338c --- /dev/null +++ b/tests/swoole_server/close_reset.phpt @@ -0,0 +1,85 @@ +--TEST-- +swoole_server: close with reset +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(['socket_buffer_size' => 128 * 1024]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $client->send("close"); + Co::sleep(1); + $data = ''; + + while (true) { + $ret = $client->recv(); + if (empty($ret)) { + break; + } + $data .= $ret; + if (substr($ret, -2, 2) == "\r\n") { + break; + } + } + Assert::lessThan(strlen($data), N); + echo "DONE\n"; + }); + Swoole\Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set([ + 'worker_num' => 1, + 'log_file' => TEST_LOG_FILE, + 'kernel_socket_send_buffer_size' => 128 * 1024, + 'socket_buffer_size' => 8 * 1024 * 1024, + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) { + $serv->send($fd, str_repeat('A', N) . "\r\n"); + Assert::eq($serv->stats()['connection_num'], 1); + phpt_var_dump("close[0]"); + Assert::true($serv->close($fd)); + usleep(50000); + phpt_var_dump("close[1]"); + Assert::false($serv->close($fd)); + Assert::eq(swoole_last_error(), SWOOLE_ERROR_SESSION_CLOSED); + Assert::eq($serv->stats()['connection_num'], 1); + Timer::after(100, function () use ($fd, $serv) { + phpt_var_dump("close[2]"); + $serv->close($fd, true); + usleep(50000); + Assert::eq($serv->stats()['connection_num'], 0); + }); + }); + $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) { + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/command.phpt b/tests/swoole_server/command.phpt new file mode 100644 index 00000000000..6e894c6e31a --- /dev/null +++ b/tests/swoole_server/command.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_server: command [1] +--SKIPIF-- + +--FILE-- +addCommand('test_getpid', SWOOLE_SERVER_COMMAND_MASTER | SWOOLE_SERVER_COMMAND_EVENT_WORKER, + function ($server) { + return json_encode(['pid' => posix_getpid()]); + }); +$server->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, +]); + +$server->on('start', function (Server $serv) { + $result = $serv->command('test_getpid', 0, SWOOLE_SERVER_COMMAND_MASTER, ['type' => 'master']); + Assert::eq($result['pid'], $serv->getMasterPid()); + $result = $serv->command('test_getpid', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']); + Assert::eq($result['pid'], $serv->getWorkerPid(1)); + $result = $serv->command('test_not_found', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']); + Assert::false($result); + + $serv->shutdown(); +}); + +$server->on('request', function (Request $request, Response $response) { +}); +$server->start(); +echo "DONE\n"; +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..b8d04e901ac --- /dev/null +++ b/tests/swoole_server/event/before_shutdown.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_server/event: onBeforeShutdown +--SKIPIF-- + +--FILE-- +setLogFile(FILE); + +$pm->parentFunc = function ($pid) use ($pm) { +}; + +$pm->childFunc = function () use ($pm) { + $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) { + $pm->writeLog('master start'); + Event::add(STDIN, function ($fp) { + echo fread($fp, 8192); + }); + $serv->shutdown(); + }); + $serv->on("BeforeShutdown", function (Server $serv) use ($pm) { + $pm->writeLog('before master shutdown'); + Event::del(STDIN); + }); + $serv->on("shutdown", function (Server $serv) use ($pm) { + $pm->writeLog('master shutdown'); + $pm->wakeup(); + }); + $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); + +echo file_get_contents(FILE); +unlink(FILE); +?> +--EXPECT-- +master start +before master shutdown +master shutdown 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 ca46edefac7..a2f54bef5ac 100644 --- a/tests/swoole_server/event/worker_exit.phpt +++ b/tests/swoole_server/event/worker_exit.phpt @@ -29,10 +29,10 @@ $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, + 'worker_num' => 1, 'log_file' => '/dev/null', ]); @@ -41,17 +41,21 @@ $pm->childFunc = function () use ($pm, $atomic) { }); $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($atomic, $pm) { + usleep(1000); $pm->writeLog('manager start'); }); $serv->on(Constant::EVENT_WORKER_START, function (Server $serv) use ($atomic, $pm) { + if ($atomic->get() == 0) { + usleep(2000); + } $pm->writeLog('worker start, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); if ($atomic->add() == 2) { 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(); @@ -62,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 07cdf14f164..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 9012): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Manager::kill_timeout_process() (ERRNO 9012): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Manager::kill_timeout_process() (ERRNO 9012): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Manager::kill_timeout_process() (ERRNO 9012): 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 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 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 03b8007b464..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 9012): worker exit timeout, forced termination +[%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_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 b31ebadea2f..72fd9da61ba 100644 --- a/tests/swoole_server/send_2.phpt +++ b/tests/swoole_server/send_2.phpt @@ -8,6 +8,8 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { @@ -32,23 +34,24 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::assert($data); $char = chr(ord('A') + $n % 10); $info = unpack('Nlen', substr($data, 0, 4)); - -// echo "c=$i, n=$n, len={$info['len']}\n---------------------------------------------------------------------\n"; + if (VERBOSE) { + echo "[Client] c=$i, n=$n, len={$info['len']}\n---------------------------------------------------------------------\n"; + } Assert::same($info['len'], strlen($data) - 4); Assert::same(str_repeat($char, 1024), substr($data, rand(4, $info['len'] - 1024 - 4), 1024)); $total += strlen($data); } }); } - swoole_event::wait(); - echo $total." bytes\n"; + Swoole\Event::wait(); + echo $total . " bytes\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => IS_IN_TRAVIS ? 2 : 4, + "worker_num" => 1, 'log_level' => SWOOLE_LOG_ERROR, 'open_length_check' => true, 'package_max_length' => 4 * 1024 * 1024, @@ -61,19 +64,23 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $serv->on('connect', function (Swoole\Server $serv, $fd, $rid) { -// echo "new client, fd=$fd\n"; + if (VERBOSE) { + echo "new client, fd=$fd\n"; + } $n = MAX_REQUESTS; while ($n--) { $len = rand(8192, 1024 * 1024); $send_data = str_repeat(chr(ord('A') + $n % 10), $len); + if (VERBOSE) { + echo "[Server] c=$fd, n=$n, len=" . (strlen($send_data) + 4) . "\n---------------------------------------------------------------------\n"; + } $retval = $serv->send($fd, pack('N', $len) . $send_data); if ($retval === false) { - echo "send error, code=".swoole_last_error()."\n"; + echo "send error, code=" . swoole_last_error() . "\n"; } } }); $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { - }); $serv->start(); }; 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/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/slow_worker.phpt b/tests/swoole_server/slow_worker.phpt index 1aafc4af0db..3c0dab84314 100644 --- a/tests/swoole_server/slow_worker.phpt +++ b/tests/swoole_server/slow_worker.phpt @@ -45,7 +45,7 @@ $pm->parentFunc = function ($pid) use ($pm, $counter_server, $counter_client, $d $pm->childFunc = function () use ($pm, $counter_server, $counter_client) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'log_file' => '/dev/null', 'open_length_check' => true, 'package_max_length' => 16 * 1024 * 1024, 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/stats_file.phpt b/tests/swoole_server/stats_file.phpt index c58bca9302e..62a438c7ae4 100644 --- a/tests/swoole_server/stats_file.phpt +++ b/tests/swoole_server/stats_file.phpt @@ -9,22 +9,30 @@ require __DIR__ . '/../include/bootstrap.php'; use function Swoole\Coroutine\run; const STATS_FILE = __DIR__ . '/stats.log'; -if (is_file(STATS_FILE)) { - unlink(STATS_FILE); -} +$rm_fn = function () { + if (is_file(STATS_FILE)) { + unlink(STATS_FILE); + } +}; +$rm_fn(); $pm = new ProcessManager; $pm->initFreePorts(1); -$pm->parentFunc = function ($pid) use ($pm) -{ - run(function() use ($pm, $pid) { +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm, $pid) { httpRequest('http://127.0.0.1:' . $pm->getFreePort(0)); for ($i = 0; $i < 4; ++$i) { Co::sleep(0.5); $content = @file_get_contents(STATS_FILE); if ('' != $content) { - echo $content; + $stats = []; + swoole_string($content)->split("\n")->each(function ($value, $key) use (&$stats) { + [$k, $v] = swoole_string($value)->split(":"); + $stats[$k] = trim($v); + }); + Assert::keyExists($stats, 'connection_num'); + Assert::keyExists($stats, 'request_count'); break; } } @@ -33,11 +41,10 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $mode = SERVER_MODE_RANDOM; $worker_num = rand(1, 4); - echo "mode: $mode\nworker_num: $worker_num\n"; + phpt_var_dump("mode: $mode\nworker_num: $worker_num\n"); $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), $mode); $server->set([ 'stats_file' => STATS_FILE, @@ -48,27 +55,13 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $server->on('request', function ($request, $response) { - $response->end("

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

"); + $response->end("

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

"); }); $server->start(); }; $pm->childFirst(); $pm->run(); +$rm_fn(); ?> ---EXPECTF-- -mode: %d -worker_num: %d -start_time: %d -connection_num: %d -accept_count: %d -close_count: %d -worker_num: %d -idle_worker_num: %d -task_worker_num: %d -tasking_num: %d -request_count: %d -dispatch_count: %d -worker_request_count: %d -worker_dispatch_count: %d -coroutine_num: %d +--EXPECT-- diff --git a/tests/swoole_server/stats_file_json.phpt b/tests/swoole_server/stats_file_json.phpt new file mode 100644 index 00000000000..47dc6717f5c --- /dev/null +++ b/tests/swoole_server/stats_file_json.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_server: stats_file [json] +--SKIPIF-- + +--FILE-- +initFreePorts(1); + +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm, $pid) { + httpRequest('http://127.0.0.1:' . $pm->getFreePort(0)); + for ($i = 0; $i < 4; ++$i) { + Co::sleep(0.5); + $content = @file_get_contents(STATS_FILE); + if ('' != $content) { + $stats = json_decode($content, true); + assert_server_stats($stats); + break; + } + } + }); + echo "\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $mode = SERVER_MODE_RANDOM; + $worker_num = rand(1, 4); + phpt_var_dump("mode: $mode\nworker_num: $worker_num\n"); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), $mode); + $server->set([ + 'stats_file' => STATS_FILE, + 'log_file' => DEV_NULL, + 'worker_num' => $worker_num, + ]); + $server->on('ManagerStart', function ($serv) use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function ($request, $response) { + $response->end("

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

"); + }); + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +$rm_fn(); +?> +--EXPECT-- diff --git a/tests/swoole_server/stats_file_php.phpt b/tests/swoole_server/stats_file_php.phpt new file mode 100644 index 00000000000..6f95430bd0e --- /dev/null +++ b/tests/swoole_server/stats_file_php.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_server: stats_file [php] +--SKIPIF-- + +--FILE-- +initFreePorts(1); + +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm, $pid) { + httpRequest('http://127.0.0.1:' . $pm->getFreePort(0)); + for ($i = 0; $i < 4; ++$i) { + Co::sleep(0.5); + if (!is_file(STATS_FILE)) { + continue; + } + $stats = include STATS_FILE; + if (empty($stats)) { + assert_server_stats($stats); + break; + } + } + }); + echo "\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $mode = SERVER_MODE_RANDOM; + $worker_num = rand(1, 4); + phpt_var_dump("mode: $mode\nworker_num: $worker_num\n"); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), $mode); + $server->set([ + 'stats_file' => STATS_FILE, + 'log_file' => DEV_NULL, + 'worker_num' => $worker_num, + ]); + $server->on('ManagerStart', function ($serv) use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function ($request, $response) { + $response->end("

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

"); + }); + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); + +$rm_fn(); +?> +--EXPECT-- 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 7d05fcc575e..ede6b91a3be 100644 --- a/tests/swoole_server/task/task_co.phpt +++ b/tests/swoole_server/task/task_co.phpt @@ -9,8 +9,7 @@ Swoole\Runtime::enableCoroutine(); $pm = new SwooleTest\ProcessManager; $randoms = []; -for ($n = MAX_REQUESTS; $n--;) -{ +for ($n = MAX_REQUESTS; $n--;) { $randoms[] = random_bytes(mt_rand(0, 65536)); } @@ -27,8 +26,9 @@ $pm->parentFunc = function () use ($pm) { 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' => 4, @@ -38,37 +38,35 @@ $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) { - $n = $request->get['n']; - switch ($request->server['path_info']) - { - case '/task': - { - list($ret_n, $ret_random) = $server->taskCo([$n], 1)[0]; - if ($ret_n !== $n) + $server->on('request', + function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { + $n = $request->get['n']; + switch ($request->server['path_info']) { + case '/task': { - $response->end("ERROR MATCH {$ret_n} with {$n}"); - return; + list($ret_n, $ret_random) = $server->taskCo([$n], 1)[0]; + if ($ret_n !== $n) { + $response->end("ERROR MATCH {$ret_n} with {$n}"); + return; + } elseif ($ret_random !== $randoms[$n]) { + $response->end("ERROR EQUAL {$ret_n}(" . strlen($ret_random) . ") with {$n}(" . strlen($randoms[$n]) . ")"); + return; + } + $response->end('OK'); + break; } - elseif ($ret_random !== $randoms[$n]) + case '/random': { - $response->end("ERROR EQUAL {$ret_n}(" . strlen($ret_random) . ") with {$n}(" . strlen($randoms[$n]) . ")"); - return; + $response->end($randoms[$n]); + break; } - $response->end('OK'); - break; } - case '/random': - { - $response->end($randoms[$n]); - break; - } - } + }); + $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('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 () { }); - $server->on('finish', function () { }); $server->start(); }; $pm->childFirst(); 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 new file mode 100644 index 00000000000..25bf7146ebe --- /dev/null +++ b/tests/swoole_server/task/timer.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_server/task: timer +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + run(function () use ($pm) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['websocket_compression' => true, ]); + $cli->upgrade('/'); + $cli->push('Hello Swoole'); + $data = $cli->recv(5); + Assert::eq($data->data, 'OK'); + echo "DONE\n"; + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'task_worker_num' => 1, + 'event_object' => true, + 'log_file' => '/dev/null', + ]); + $http->on('message', function (Server $server, Frame $frame) { + $server->task(['fd' => $frame->fd]); + }); + $http->on('WorkerStart', function (Server $server, int $workerId) { + if ($server->taskworker) { + Swoole\Timer::after(1, function () use ($server, $workerId) { + var_dump("after1 : " . time()); + }); + // never callback + 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) { + var_dump('end : ' . time()); + Assert::true($server->push($task->data['fd'], "OK")); + }); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +string(19) "after1 : %d" +string(18) "begin : %d" +string(16) "end : %d" +DONE 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 1de7607e3a0..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 = []; @@ -17,8 +17,8 @@ $map = []; while($n--) { $key = "key-".rand(1000000, 9999999); $value = RandStr::getBytes(rand(100*1024, 250*1024)); - $data[$key] = $value; - $table->set($key, ['string' => $value]); + $map[$key] = $value; + $table->set($key, ['string' => $value]); } foreach($map as $k => $v) { diff --git a/tests/swoole_table/stats.phpt b/tests/swoole_table/stats.phpt new file mode 100644 index 00000000000..6c8097e6358 --- /dev/null +++ b/tests/swoole_table/stats.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_table: stats +--SKIPIF-- + +--FILE-- +column('string', Table::TYPE_STRING, 256); +$table->create(); + +$map = []; +$keys = []; + +$n = N; +while ($n--) { + $key = base64_decode(RandStr::getBytes(rand(10, 30))); + $value = RandStr::getBytes(rand(100, 250)); + if ($table->set($key, ['string' => $value])) { + $map[$key] = $value; + $keys[] = $key; + } +} + +$stats1 = $table->stats(); + +Assert::eq(count($keys), N); +Assert::eq(count(array_unique($keys)), $stats1['insert_count']); + +phpt_var_dump("insert\n".str_repeat('-', 64), $stats1); + +define('UPDATE_N', rand(100, 1000)); + +$_n = UPDATE_N; +while ($_n--) { + $key = array_rand($map); + $value = RandStr::getBytes(rand(100, 250)); + Assert::true($table->set($key, ['string' => $value])); + $map[$key] = $value; +} + +$stats2 = $table->stats(); +Assert::eq($stats1['update_count'] + UPDATE_N, $stats2['update_count']); +phpt_var_dump("update\n" . str_repeat('-', 64), $stats2); + +foreach($map as $k => $v) { + Assert::same($table->get($k)['string'], $v); + $table->del($k); +} + +$stats3 = $table->stats(); +Assert::eq($stats3['num'], 0); +Assert::eq($stats3['available_slice_num'], $stats3['total_slice_num']); +phpt_var_dump("delete\n" . str_repeat('-', 64), $stats3); +?> +--EXPECT-- diff --git a/tests/swoole_table/type_conv.phpt b/tests/swoole_table/type_conv.phpt new file mode 100644 index 00000000000..0900f9c58ef --- /dev/null +++ b/tests/swoole_table/type_conv.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_table: type convert +--SKIPIF-- + +--FILE-- +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"; +} + +$table->set('test_key', array('id' => 1, 'name' => NAME, 'num' => PI)); +$table->set(1002, array('id' => '2', 'name' => 'hello', 'num' => PI + 9)); + +$r1 = ($table->get('test_key')); +$r2 = ($table->get(1002)); + +Assert::same($r1['id'], 1); +Assert::same($r2['id'], 2); + +$table->set('test_key', array('id' => '2348', 'name' => 1024, 'num' => '3.231')); +$r1 = ($table->get('test_key')); + +Assert::same($r1['id'], 2348); +Assert::same($r1['num'], 3.231); +Assert::same($r1['name'], '1024'); + +$table->set('test_key', array('id' => 'abc', 'name' => 1024, 'num' => '3.231')); +$r1 = ($table->get('test_key')); +Assert::same($r1['id'], 0); + +?> +--EXPECT-- 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 new file mode 100644 index 00000000000..edff6ec0501 --- /dev/null +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -0,0 +1,124 @@ +/* + Copyright Jiaxun Yang 2018. + 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 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 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 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_mips64_n64_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 3 +.type swoole_jump_fcontext,@function +.ent swoole_jump_fcontext +swoole_jump_fcontext: + # reserve space on stack + daddiu $sp, $sp, -160 + + sd $s0, 64($sp) # save S0 + sd $s1, 72($sp) # save S1 + sd $s2, 80($sp) # save S2 + sd $s3, 88($sp) # save S3 + sd $s4, 96($sp) # save S4 + sd $s5, 104($sp) # save S5 + sd $s6, 112($sp) # save S6 + sd $s7, 120($sp) # save S7 + sd $fp, 128($sp) # save FP + sd $ra, 144($sp) # save RA + sd $ra, 152($sp) # save RA as PC + +#if defined(__mips_hard_float) + s.d $f24, 0($sp) # save F24 + s.d $f25, 8($sp) # save F25 + s.d $f26, 16($sp) # save F26 + s.d $f27, 24($sp) # save F27 + s.d $f28, 32($sp) # save F28 + s.d $f29, 40($sp) # save F29 + s.d $f30, 48($sp) # save F30 + s.d $f31, 56($sp) # save F31 +#endif + + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp + + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 + +#if defined(__mips_hard_float) + l.d $f24, 0($sp) # restore F24 + l.d $f25, 8($sp) # restore F25 + l.d $f26, 16($sp) # restore F26 + l.d $f27, 24($sp) # restore F27 + l.d $f28, 32($sp) # restore F28 + l.d $f29, 40($sp) # restore F29 + l.d $f30, 48($sp) # restore F30 + l.d $f31, 56($sp) # restore F31 +#endif + + ld $s0, 64($sp) # restore S0 + ld $s1, 72($sp) # restore S1 + ld $s2, 80($sp) # restore S2 + ld $s3, 88($sp) # restore S3 + ld $s4, 96($sp) # restore S4 + ld $s5, 104($sp) # restore S5 + ld $s6, 112($sp) # restore S6 + ld $s7, 120($sp) # restore S7 + ld $fp, 128($sp) # restore FP + ld $ra, 144($sp) # restore RAa + + # load PC + ld $t9, 152($sp) + + # adjust stack + daddiu $sp, $sp, 160 + + 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 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 new file mode 100644 index 00000000000..a2f9a2f3bb3 --- /dev/null +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -0,0 +1,150 @@ +/* + 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| * + * ------------------------------------------------- * + * | fs0 | fs1 | fs2 | fs3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | fs4 | fs5 | fs6 | fs7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | fs8 | fs9 | fs10 | fs11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | s0 | s1 | s2 | s3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | s4 | s5 | s6 | s7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| * + * ------------------------------------------------- * + * | s8 | s9 | s10 | s11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | | | | | * + * ------------------------------------------------- * + * | 0xc0| 0xc4| 0xc8| 0xcc| | | | | * + * ------------------------------------------------- * + * | ra | pc | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "jump_riscv64_sysv_elf_gas.S" +.text +.align 1 +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: + # prepare stack for GP + FPU + addi sp, sp, -0xd0 + + # save fs0 - fs11 + fsd fs0, 0x00(sp) + fsd fs1, 0x08(sp) + fsd fs2, 0x10(sp) + fsd fs3, 0x18(sp) + fsd fs4, 0x20(sp) + fsd fs5, 0x28(sp) + fsd fs6, 0x30(sp) + fsd fs7, 0x38(sp) + fsd fs8, 0x40(sp) + fsd fs9, 0x48(sp) + fsd fs10, 0x50(sp) + fsd fs11, 0x58(sp) + + # save s0-s11, ra + sd s0, 0x60(sp) + sd s1, 0x68(sp) + sd s2, 0x70(sp) + sd s3, 0x78(sp) + sd s4, 0x80(sp) + sd s5, 0x88(sp) + sd s6, 0x90(sp) + sd s7, 0x98(sp) + sd s8, 0xa0(sp) + sd s9, 0xa8(sp) + sd s10, 0xb0(sp) + sd s11, 0xb8(sp) + sd ra, 0xc0(sp) + + # save RA as PC + sd ra, 0xc8(sp) + + # store SP (pointing to context-data) in A2 + mv a2, sp + + # restore SP (pointing to context-data) from A0 + mv sp, a0 + + # load fs0 - fs11 + fld fs0, 0x00(sp) + fld fs1, 0x08(sp) + fld fs2, 0x10(sp) + fld fs3, 0x18(sp) + fld fs4, 0x20(sp) + fld fs5, 0x28(sp) + fld fs6, 0x30(sp) + fld fs7, 0x38(sp) + fld fs8, 0x40(sp) + fld fs9, 0x48(sp) + fld fs10, 0x50(sp) + fld fs11, 0x58(sp) + + # load s0-s11,ra + ld s0, 0x60(sp) + ld s1, 0x68(sp) + ld s2, 0x70(sp) + ld s3, 0x78(sp) + ld s4, 0x80(sp) + ld s5, 0x88(sp) + ld s6, 0x90(sp) + ld s7, 0x98(sp) + ld s8, 0xa0(sp) + ld s9, 0xa8(sp) + ld s10, 0xb0(sp) + ld s11, 0xb8(sp) + ld ra, 0xc0(sp) + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + mv a0, a2 + + # load pc + ld a2, 0xc8(sp) + + # restore stack from GP + FPU + addi sp, sp, 0xd0 + + 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_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 new file mode 100644 index 00000000000..d3d46313b6b --- /dev/null +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -0,0 +1,96 @@ +/* + Copyright Jiaxun Yang 2018. + 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 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 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 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_mips64_n64_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 3 +.type swoole_make_fcontext,@function +.ent swoole_make_fcontext +swoole_make_fcontext: +#ifdef __PIC__ +.set noreorder +.cpload $t9 +.set reorder +#endif + # shift address in A0 to lower 16 byte boundary + li $v1, 0xfffffffffffffff0 + and $v0, $v1, $a0 + + # reserve space for context-data on context-stack + daddiu $v0, $v0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + sd $a2, 152($v0) + # save global pointer in context-data + sd $gp, 136($v0) + + # psudo instruction compute abs address of label finish based on GP + dla $t9, finish + + # save address of finish as return-address for context-function + # will be entered after context-function returns + sd $t9, 144($v0) + + jr $ra # return pointer to context-data + +finish: + # reload our gp register (needed for la) + daddiu $t0, $sp, -160 + ld $gp, 136($t0) + + # call _exit(0) + # the previous function should have left the 16 bytes incoming argument + # area on the stack which we reuse for calling _exit + dla $t9, _exit + move $a0, $zero + jr $t9 +.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 new file mode 100644 index 00000000000..cd43c775447 --- /dev/null +++ b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S @@ -0,0 +1,91 @@ +/* + 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| * + * ------------------------------------------------- * + * | fs0 | fs1 | fs2 | fs3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | fs4 | fs5 | fs6 | fs7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | fs8 | fs9 | fs10 | fs11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | s0 | s1 | s2 | s3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | s4 | s5 | s6 | s7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| * + * ------------------------------------------------- * + * | s8 | s9 | s10 | s11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | | | | | * + * ------------------------------------------------- * + * | 0xc0| 0xc4| 0xc8| 0xcc| | | | | * + * ------------------------------------------------- * + * | ra | pc | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "make_riscv64_sysv_elf_gas.S" +.text +.align 1 +.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 swoole_make_fcontext() == address of context-function + # store address as a PC to jump in + sd a2, 0xc8(a0) + + # save address of finish as return-address for context-function + # will be entered after context-function returns (RA register) + lla a4, finish + sd a4, 0xc0(a0) + + ret // return pointer to context-data (a0) + +finish: + # exit code is zero + li a0, 0 + # exit application + tail _exit@plt + +.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..fe4d503d011 --- /dev/null +++ b/thirdparty/nghttp2/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +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. \ No newline at end of file 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